HQL Error Reporting System

This document describes how HQL detects, reports, and maps errors back to your original source code.

Table of Contents

  1. Overview
  2. Error Categories
  3. The Compilation Pipeline
  4. Position Tracking
  5. Source Map Chain
  6. Type Error Integration
  7. Error Message Format
  8. Error Codes Reference
  9. Troubleshooting

Overview

HQL reports errors from parsing, import resolution, transformation, type checking, code generation, and runtime execution. When something goes wrong, HQL reports the available:

  • What went wrong (clear error message)
  • Where it happened (file, line, column)
  • Why it might have happened (suggestions)
  • How to fix it (when possible)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Example Error Output                                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  error[HQL1003] : Unexpected ')' - Check for a missing opening '(' ...        β”‚
β”‚    --> src/math.hql:5:24                                                     β”‚
β”‚     β”‚                                                                        β”‚
β”‚   5 β”‚ (fn add [a, b] (+ a b)))                                               β”‚
β”‚     β”‚                        ^ unmatched `)`                                 β”‚
β”‚     β”‚                                                                        β”‚
β”‚    help: Check for missing opening parenthesis '(' earlier in the code.      β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Error Categories

HQL has four main categories of errors, each occurring at different stages:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         ERROR CATEGORIES                                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚  β”‚   PARSE     β”‚   β”‚ TRANSFORM   β”‚   β”‚    TYPE     β”‚   β”‚   RUNTIME   β”‚     β”‚
β”‚  β”‚   ERRORS    │──▢│   ERRORS    │──▢│   ERRORS    │──▢│   ERRORS    β”‚     β”‚
β”‚  β”‚             β”‚   β”‚             β”‚   β”‚             β”‚   β”‚             β”‚     β”‚
β”‚  β”‚ HQL1xxx     β”‚   β”‚ HQL4xxx     β”‚   β”‚ TSxxxx      β”‚   β”‚ HQL5xxx     β”‚     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”‚        β”‚                 β”‚                 β”‚                 β”‚              β”‚
β”‚        β–Ό                 β–Ό                 β–Ό                 β–Ό              β”‚
β”‚   Syntax issues    IR generation     TypeScript        Execution           β”‚
β”‚   Invalid tokens   Macro expansion   type checker      exceptions          β”‚
β”‚   Mismatched ( )   Unknown forms     Type mismatch     Undefined vars      β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1. Parse Errors (HQL1xxx)

Occur when HQL cannot understand your code's structure.

// Missing closing parenthesis
(fn add [a, b] (+ a b)   // ← HQL1001: Unclosed list

// Unterminated string literal
(print "oops)           // ← HQL1002: Unclosed string

// Stray closing parenthesis
(+ 1 2))                // ← HQL1003: Unexpected ')'

2. Import Errors (HQL2xxx)

Occur when an imported module cannot be resolved.

// File does not exist
(import [helper] from "./missing.hql")
(helper)                               // ← error[HQL2002]: Module not found

3. Type Errors (HQL6004 / HQL610x)

Occur when the type checker finds type mismatches. HQL uses TypeScript's checker as the backend but rewrites diagnostics into HQL-native error codes such as HQL6101 and HQL6104 under error[HQL6004]: Type checking failed. The raw TSxxxx code is not shown to you. Current hql run and hql compile both fail closed when type checking fails; JavaScript is not emitted or executed.

// Type mismatch
(fn add [a:number, b:number] -> number (+ a b))
(add "hello" "world")   // ← error[HQL6101]: Expected number but got string

4. Runtime Errors (HQL5xxx)

Occur when code executes but encounters a problem.

// Undefined variable (hyphens normalize to underscores in the message)
(print unknown-var)     // ← HQL5001: unknown_var is not defined

The Compilation Pipeline

Understanding the pipeline helps you understand where errors come from:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        HQL COMPILATION PIPELINE                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

     YOUR CODE                                                    OUTPUT
         β”‚                                                           β–²
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚   math.hql      β”‚  Your HQL source file                           β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 1: PARSING                                         β”‚
         β”‚  ─────────────────                                        β”‚
         β”‚  β€’ Tokenize source code                                   β”‚
         β”‚  β€’ Build Abstract Syntax Tree (AST)                       β”‚
         β”‚  β€’ Track line:column for each token                       β”‚
         β”‚  β€’ ERRORS: HQL1xxx (syntax errors)                        β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚   AST           β”‚  Tree structure of your code                     β”‚
β”‚   (with pos)    β”‚  Each node has position info                     β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 2: TRANSFORMATION                                  β”‚
         β”‚  ───────────────────────                                  β”‚
         β”‚  β€’ Expand macros                                          β”‚
         β”‚  β€’ Convert to IR (Intermediate Representation)            β”‚
         β”‚  β€’ Preserve positions through transforms                  β”‚
         β”‚  β€’ ERRORS: HQL4xxx (transform errors)                     β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚   IR            β”‚  Intermediate Representation                     β”‚
β”‚   (with pos)    β”‚  Normalized code structure                       β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 3: TYPESCRIPT GENERATION                           β”‚
         β”‚  ──────────────────────────────                           β”‚
         β”‚  β€’ Generate TypeScript code                               β”‚
         β”‚  β€’ Include type annotations                               β”‚
         β”‚  β€’ Create SOURCE MAP 1 (HQL β†’ TS)                         β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚  generated.ts   β”‚  TypeScript code                                 β”‚
β”‚  + source map   β”‚  Maps back to HQL positions                      β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 4: TYPE CHECKING                                   β”‚
         β”‚  ──────────────────────                                   β”‚
         β”‚  β€’ TypeScript compiler analyzes types                     β”‚
         β”‚  β€’ Reports type mismatches                                β”‚
         β”‚  β€’ We map positions back to HQL                           β”‚
         β”‚  β€’ ERRORS: TSxxxx (type errors)                           β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚  Type Errors    β”‚  Mapped to HQL positions                         β”‚
β”‚  (fatal)        β”‚  Displayed to user                               β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 5: JAVASCRIPT COMPILATION                          β”‚
         β”‚  ───────────────────────────────                          β”‚
         β”‚  β€’ TypeScript compiles to JavaScript                      β”‚
         β”‚  β€’ Creates SOURCE MAP 2 (TS β†’ JS)                         β”‚
         β”‚  β€’ We chain: HQL β†’ TS β†’ JS = HQL β†’ JS                     β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚   output.js     β”‚  Final JavaScript                                β”‚
β”‚   + source map  β”‚  Chained map points to HQL                       β”‚
β”‚                 β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                  β”‚
         β”‚                                                           β”‚
         β”‚  PHASE 6: EXECUTION                                       β”‚
         β”‚  ──────────────────                                       β”‚
         β”‚  β€’ JavaScript runs                                        β”‚
         β”‚  β€’ Runtime errors occur here                              β”‚
         β”‚  β€’ Stack traces mapped to HQL                             β”‚
         β”‚  β€’ ERRORS: HQL5xxx (runtime errors)                       β”‚
         β–Ό                                                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                  β”‚
β”‚                 β”‚                                                  β”‚
β”‚   Result or     β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚   Runtime Error β”‚
β”‚                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Position Tracking

HQL tracks the original position (line and column) of every piece of code through all transformations.

How Positions Flow Through the Pipeline

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        POSITION TRACKING FLOW                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  Original HQL:
  ─────────────
  Line 3: (print (add "wrong" 5))
                      β–²
                      └── Position: line=3, column=18

  After Parsing (AST):
  ────────────────────
  CallExpression {
    callee: "add",
    arguments: [
      StringLiteral {
        value: "wrong",
        position: { line: 3, column: 18 }   ← Position preserved!
      },
      NumberLiteral { value: 5, position: { line: 3, column: 26 } }
    ],
    position: { line: 3, column: 9 }
  }

  After Transform (IR):
  ─────────────────────
  IRCallExpression {
    callee: IRIdentifier { name: "add" },
    arguments: [
      IRStringLiteral {
        value: "wrong",
        position: { line: 3, column: 18 }   ← Still preserved!
      },
      ...
    ]
  }

  Generated TypeScript:
  ─────────────────────
  console.log(add("wrong", 5))//
                  β–²
                  └── Generated at: line=7, column=17
                      Maps to HQL: line=3, column=18

  Source Map Entry:
  ─────────────────
  {
    generated: { line: 7, column: 17 },
    original: { line: 3, column: 18 },
    source: "math.hql"
  }

The emit() Function

Position tracking happens in the TypeScript generator:

// In ir-to-typescript.ts
private emit(text: string, irPosition?: IR.SourcePosition): void {
  // Record mapping if we have original position
  if (irPosition && irPosition.line !== undefined) {
    this.mappings.push({
      generated: {
        line: this.currentLine,
        column: this.currentColumn
      },
      original: {
        line: irPosition.line,
        column: irPosition.column
      },
      source: irPosition.filePath
    })//
  }

  // Update current position in generated code
  this.code += text//
  // ... update currentLine/currentColumn
}

Source Map Chain

HQL creates a chain of source maps to trace from final JavaScript back to original HQL:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          SOURCE MAP CHAIN                                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    math.hql                  generated.ts                output.js
    ────────                  ────────────                ─────────

    Line 3                    Line 7                      Line 5
    Column 18                 Column 17                   Column 12
         β”‚                         β”‚                           β”‚
         β”‚                         β”‚                           β”‚
         β”‚     SOURCE MAP 1        β”‚      SOURCE MAP 2         β”‚
         β”‚     (HQL β†’ TS)          β”‚      (TS β†’ JS)            β”‚
         β”‚                         β”‚                           β”‚
         β–Ό                         β–Ό                           β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  HQL    │─────────────▢│   TS    │──────────────▢│   JS    β”‚
    β”‚ Source  β”‚              β”‚ Source  β”‚               β”‚ Output  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–²                                                  β”‚
         β”‚                                                  β”‚
         β”‚              CHAINED SOURCE MAP                  β”‚
         β”‚              (HQL β†’ JS)                          β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β–Ό
                    When error at JS 5:12
                    We look up: JS β†’ TS β†’ HQL
                    Result: HQL 3:18

Chaining Process

// In source-map-chain.ts
export async function chainSourceMaps(
  hqlToTsMappings: SourceMapping[], // Map 1: HQL β†’ TS
  tsToJsMapJson: string, // Map 2: TS β†’ JS (from tsc)
  hqlSourcePath: string,
): Promise<ChainedSourceMap> {
  // Build lookup: TS position β†’ HQL position
  const tsToHqlMap = new Map<string, Position>(); //
  for (let mapping of hqlToTsMappings) {
    const key = `${mapping.generated.line}:${mapping.generated.column}`; //
    tsToHqlMap.set(key, mapping.original); //
  }

  // For each JS β†’ TS mapping, look up TS β†’ HQL
  // Result: JS β†’ HQL (chained)
}

Type Error Integration

HQL leverages TypeScript's type checker as a "free" type system:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     TYPE ERROR FLOW                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  1. YOUR HQL CODE
  ─────────────────
  (fn add [a:number, b:number] -> number (+ a b))
  (print (add "wrong" 5))
              β–²
              └── You made a type error here!


  2. GENERATED TYPESCRIPT
  ───────────────────────
  function add(a: number, b: number): number {
    return a + b//
  }
  console.log(add("wrong", 5))//
                  β–²
                  └── TypeScript sees this


  3. TYPESCRIPT TYPE CHECKER
  ──────────────────────────
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  TypeScript Compiler (tsc)                                             β”‚
  β”‚                                                                        β”‚
  β”‚  "I found an error!"                                                   β”‚
  β”‚                                                                        β”‚
  β”‚  {                                                                     β”‚
  β”‚    message: "Argument of type 'string' is not assignable               β”‚
  β”‚              to parameter of type 'number'",                           β”‚
  β”‚    code: 2345,                                                         β”‚
  β”‚    file: "generated.ts",                                               β”‚
  β”‚    line: 4,                                                            β”‚
  β”‚    column: 17                                                          β”‚
  β”‚  }                                                                     β”‚
  β”‚                                                                        β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


  4. TRANSLATE + MAP BACK TO HQL
  ──────────────────────────────
  Source map lookup: TS 4:17 β†’ HQL 2:13
  TS2345 message rewritten into an HQL-native `HQL6101` diagnostic
  (the raw `TSxxxx` code is not shown to the user).


  5. DISPLAY TO USER
  ──────────────────
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                                                                        β”‚
  β”‚  error[HQL6101] at math.hql:2:13: Expected number but got string       β”‚
  β”‚                                                                        β”‚
  β”‚     2 β”‚ (print (add "wrong" 5))                                        β”‚
  β”‚       β”‚              β–²β–²β–²β–²β–²β–²β–²                                           β”‚
  β”‚                                                                        β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Type Errors Fail Closed

On current hql run, type errors fail before JavaScript execution:

$ deno run -A src/hql/cli.ts run math.hql

error[HQL6004]: Type checking failed:
math.hql:2:13 - error[HQL6101]: Expected number but got string

On hql compile, the same type errors fail the build and no JavaScript is emitted:

$ deno run -A src/hql/cli.ts compile math.hql -o out.js

error[HQL6004]: Type checking failed:
math.hql:2:13 - error[HQL6101]: Expected number but got string

Error Message Format

All HQL errors follow a consistent format:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        ERROR MESSAGE ANATOMY                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  error[HQL5001] : variable_name is not defined      ← error code + message
    --> src/app.hql:5:9                              ← file:line:column
     β”‚
   5 β”‚ (print variable-name)                         ← source context
     β”‚        ^ not found in this scope              ← caret + label
     β”‚
    help: Check that `variable_name` is spelled ...  ← suggestion (help:)

Some errors also append a note: line with extra context (for example, parse errors note that every ( must have a matching )). HQL diagnostics stay HQL-native and do not link out to upstream TypeScript documentation.

Components

ComponentDescription
Error codeerror[HQL5001] β€” unique identifier plus message
Location (-->)Full path with line:column
Source contextThe line(s) of code, with a caret under the span
help:How to fix it (a "Did you mean …?" suggestion when a
close match is found)
note:Extra context shown for some errors

Error Domains

The code prefix indicates the owning HQL boundary:

  • HQL errors come from language parsing, compilation, and runtime semantics.

Error Codes Reference

The numeric values below are the single source of truth in src/common/error-codes.ts (enum HQLErrorCode). Keep this table in sync.

Parse Errors (HQL1xxx)

CodeDescription
HQL1001Unclosed list
HQL1002Unclosed string
HQL1003Unexpected token
HQL1004Unexpected EOF
HQL1005Invalid syntax
HQL1006Unclosed comment
HQL1007Invalid character

Import Errors (HQL2xxx)

CodeDescription
HQL2001Invalid import syntax
HQL2002Module not found
HQL2003Circular import
HQL2004Invalid import path
HQL2005Import resolution failed
HQL2006Export not found

Validation Errors (HQL3xxx)

CodeDescription
HQL3001Invalid function syntax
HQL3002Invalid class syntax
HQL3003Missing required argument
HQL3004Too many arguments
HQL3005Invalid parameter
HQL3006Invalid variable name
HQL3007Duplicate parameter
HQL3008Invalid expression
HQL3009Pure effect violation

Transform Errors (HQL4xxx)

CodeDescription
HQL4001Transformation failed
HQL4002Unsupported feature
HQL4003Invalid AST node
HQL4004Transform type mismatch

Runtime Errors (HQL5xxx)

CodeDescription
HQL5001Undefined variable
HQL5002Type mismatch
HQL5003Division by zero
HQL5004Null or undefined reference
HQL5005Function not found

Codegen Errors (HQL6xxx)

CodeDescription
HQL6001Code generation failed
HQL6002Invalid codegen target
HQL6003Source map generation failed

Macro Errors (HQL7xxx)

CodeDescription
HQL7001Invalid macro definition
HQL7002Macro expansion failed
HQL7003Invalid macro syntax
HQL7004Macro not found
HQL7005Macro recursion limit

Type Errors (HQL6004 / HQL610x)

Type errors are detected by the TypeScript checker backend, then rewritten into HQL-native messages under error[HQL6004]: Type checking failed; individual diagnostics use HQL610x codes. The raw TSxxxx code is not surfaced to you. The most common backend codes and the HQL-native message they translate to:

Backend codeHQL-native message
TS2345Expected <type> but got <type>
TS2322Cannot use <type> where <type> is expected
TS2304Undefined binding: <name>
TS2339Property '<x>' does not exist on type '<y>'
TS2554Function expected N argument(s), got M

Troubleshooting

Error Position Seems Wrong

If an error points to the wrong location:

  1. Check for macros - Macro-generated code might have positions from macro definition
  2. Check for multi-byte characters - Unicode might affect column counting
  3. Check imports - Error might be in an imported file

Type Error Not Detected

If a type error isn't being caught:

  1. Add type annotations - HQL uses gradual typing; untyped code isn't checked
  2. Annotate rest parameters - Prefer ...rest:Array<T> so the variadic tail is typed
  3. Verify syntax - Type annotations attach to the binding with name:Type
// Annotate the parameters you want checked
(fn add [a:number, b:number] -> number (+ a b))

// Commas between parameters are optional
(fn add [a:number b:number] -> number (+ a b))

Stack Trace Points to Generated Code

If runtime errors show JavaScript line numbers:

  1. Source maps should handle this - Check that .js.map file exists
  2. Use the HQL CLI - Directly running generated JavaScript might not load source maps
  3. Report a bug - If positions are wrong, please report it

See Also


Production Readiness

This section documents the verified behavior and known limitations of HQL's error reporting system.

Current Verified Status

These checks are intentionally scoped to behavior proven by the current test suite. Do not describe the system as "100% accurate" unless every compiler, runtime, bundled-output, and host-integration path has fresh black-box proof.

SurfaceVerified behavior
hql run runtime errorsMaps real-file runtime failures back to .hql source with highlighted context.
Imported HQL runtime errorsMaps failures in imported .hql modules to the imported source file.
Legacy HQL CLI parse errorsPrints error[HQL1001], source file, line/column, caret context, help, and note.
Run-path type diagnosticsFails closed before execution, displays HQL-native type diagnostics, and uses 1-based HQL columns.
Compile-path type diagnosticsFails closed, does not emit the target JS, displays 1-based HQL columns, and avoids duplicate run-warning output.
Compiled JS source mapsFinal bundled inline maps include original .hql sources, and direct deno run compiled.js stack traces point back to .hql rather than .hql-cache/*.ts.
Runtime fallback precisionRuntime location resolution no longer invents exact HQL source locations through keyword-search fallback when no source map or metadata can prove the position.

Known Limitations

The system now has meaningful black-box coverage for the main CLI and compiled JavaScript paths, but it is still not a Rust/Swift-grade diagnostic system. Remaining maturity work includes multi-span type diagnostics, richer type-error source context, and broader host-level verification outside the focused HQL CLI and Deno execution paths.

Historical Limitations

1. User-Defined Macro Positions (Same File)

Status: βœ… Fixed (December 2024)

When a user-defined macro and its call site are in the same file, type errors in macro-expanded code may point to the macro definition instead of the call site.

This bug has been fixed. The updateMetaRecursively function in src/hql/s-exp/macro.ts now correctly updates positions when:

  1. No existing metadata
  2. Different source file (macro definition in another file)
  3. Same file but expression comes from earlier in file (macro definition)
// Example - now correctly reports line 5
(macro my-add [a, b]
  `(+ ~a ~b))           // Line 2 - macro definition

(fn check [x:number] -> number x)
(check (my-add "x" 5))  // Line 5 - call site

// Diagnostic correctly reports line 5 (the call site), e.g.
// "error[HQL6101] at test.hql:5:16: Expected number but got string"

2. Property Access On Known Types

Status: By design (gradual typing)

Accessing a missing property never throws β€” at runtime it returns undefined. Whether the checker flags the access depends on whether it can infer a type:

// The literal 42 is inferred as `number`, so the missing `.length` is flagged
// before execution.
(let x 42)
(print x.length)        // β†’ error[HQL6104]: Property 'length' does not
                        //   exist on type 'number'

// A typed parameter is checked the same way.
(fn f [x:number] -> number
  (.length x))          // β†’ error[HQL6104]: Property 'length' does not
                        //   exist on type 'number'

Workaround: Annotate values/parameters so the checker can verify property access; truly dynamic values are not checked.


Technical Implementation

For developers working on HQL itself:

ComponentFile
Parsersrc/hql/transpiler/pipeline/parser.ts
IR Generatorsrc/hql/transpiler/pipeline/syntax-transformer.ts
TS Generatorsrc/hql/transpiler/pipeline/ir-to-typescript.ts
Type Checkersrc/hql/transpiler/pipeline/ts-compiler.ts
Source Mapssrc/hql/transpiler/pipeline/source-map-chain.ts
Error Formattersrc/common/error.ts
Error Codessrc/common/error-codes.ts