# Error Handling in TypeScript-Go Code Generation: Generation-Time Validation and Runtime Safety

> Learn about error handling in typescript-go code generation. Discover generation-time validation and runtime safety strategies for robust Go code.

- Repository: [Microsoft/typescript-go](https://github.com/microsoft/typescript-go)
- Tags: deep-dive
- Published: 2026-04-25

---

**The typescript-go project implements a dual-layer error strategy that throws JavaScript errors during schema generation while emitting Go code that uses panics for programmer errors and `fmt.Errorf` returns for recoverable runtime failures.**

The microsoft/typescript-go repository transforms TypeScript AST schemas into native Go implementations through code generation. Error handling spans two distinct phases: Node.js scripts that validate the schema before emitting code, and the resulting Go binaries that parse AST nodes at runtime. This architecture separates configuration failures (which abort generation) from runtime behavior (which distinguishes between programming bugs and malformed input).

## Generation-Time Validation in Node.js Scripts

The generator script [`_scripts/generate-go-ast.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-go-ast.ts) validates **kind markers** and **union definitions** before writing any Go source. If a marker references a non-existent kind, the script throws a standard JavaScript `Error` to abort immediately:

```typescript
// _scripts/generate-go-ast.ts
// Lines 880-894
if (!definedKinds.has(marker.value) && !markerNames.has(marker.value)) {
    throw new Error(
        `Kind marker ${marker.name} references undefined kind or marker "${marker.value}"`
    );
}
...
if (!definedKinds.has(m) && !api.hasKindAlias(m)) {
    throw new Error(
        `Kind union ${kindUnion.name} references undefined kind or union "${m}"`
    );
}

```

This aggressive validation ensures that inconsistent schemas never produce invalid Go code. The generation process exits with a non-zero status code, preventing corrupted builds from reaching compilation.

## Panic-Based Error Handling in Generated Go Code

For the generated Go implementation in [`internal/ast/ast_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast_generated.go), the emitter inserts defensive panics for unreachable code paths. When generating **Update** methods for nodes with multiple aliases, the script writes a default case that panics if an unexpected `Kind` reaches the method:

```typescript
// _scripts/generate-go-ast.ts
// Lines 400-406
w.write("default:");
w.push();
w.write(`panic("unexpected kind in Update${node.name}: " + node.Kind.String())`);
w.pop();

```

These panics indicate generator bugs rather than user errors. If `UpdateIdentifier` or similar methods receive an incompatible node kind, the program crashes immediately with a descriptive message containing the offending kind value from [`internal/ast/kind_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/kind_generated.go). This fail-fast approach prevents silent data corruption during AST manipulation.

## Recoverable Runtime Errors with fmt.Errorf

Unlike Update methods, the decoder functions generated in [`internal/ast/decoder_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/decoder_generated.go) return explicit error values for gracefully handling malformed input. The encoder script [`_scripts/generate-encoder.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-encoder.ts) emits code that uses `fmt.Errorf` when encountering unknown node kinds during deserialization:

```typescript
// _scripts/generate-encoder.ts
// Lines 64-68 (inside createStringNode)
default:
    w.push();
    w.write(`return nil, fmt.Errorf("unknown string node kind %v", kind)`);
    w.pop();

```

Similar patterns appear in `createExtendedNode` and `createChildrenNode` functions. This allows callers such as the LSP server or CLI tools to capture decoding failures, log them, and continue operation rather than crashing the entire process.

## Practical Implementation Examples

The following patterns demonstrate how to interact with these error handling layers in production code.

**Catching decoder errors in Go:**

```go
node, err := decoder.Decode(data)
if err != nil {
    // Propagate the error to the LSP client or log it
    log.Printf("AST decode failed: %v", err)
    return nil, err
}

```

**Recovering from panics in Update methods:**

```go
func safeUpdateIdent(f *ast.NodeFactory, n *ast.Identifier, newName string) (node *ast.Node) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("UpdateIdentifier panic: %v", r)
            // Return the original node to keep the program alive
            node = n.AsNode()
        }
    }()
    return f.UpdateIdentifier(n, newName)
}

```

**Running the generator with error handling:**

```bash
node --experimental-strip-types _scripts/generate-go-ast.ts \
    && node --experimental-strip-types _scripts/generate-encoder.ts

# If any validation error occurs, the script exits with a non-zero code

# because the thrown Error aborts the process.

```

## Summary

- **Generation-time validation** in [`_scripts/generate-go-ast.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-go-ast.ts) throws JavaScript Errors for schema inconsistencies, aborting the build before invalid Go code is produced.
- **Panic statements** emitted into [`internal/ast/ast_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast_generated.go) serve as assertions for impossible code paths in Update methods, surfacing generator bugs immediately.
- **Recoverable errors** in [`internal/ast/decoder_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/decoder_generated.go) use `fmt.Errorf` to allow graceful handling of malformed AST input at runtime.
- The architecture distinguishes between programming errors (panics) and user input errors (returned errors), following Go idioms for error handling while maintaining strict schema integrity during the build process.

## Frequently Asked Questions

### Why does typescript-go use panics instead of errors for Update methods?

Update methods in the generated [`internal/ast/ast_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/ast_generated.go) file use panics because receiving an incompatible node kind represents a programming error in the generator itself, not a runtime condition that users should handle. By panicking with a message containing the unexpected kind value from [`internal/ast/kind_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/kind_generated.go), developers can immediately identify when the code generator has produced an incomplete switch statement or missed an alias case during development.

### How does the generator handle invalid schema definitions?

The Node.js generator scripts in [`_scripts/generate-go-ast.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-go-ast.ts) validate all kind markers and union definitions against the schema before emitting code. If a marker references a non-existent kind at lines 880-894, the script throws a JavaScript Error that aborts the entire generation process with a non-zero exit code, preventing the compilation of invalid Go source files.

### Can runtime decoder errors be recovered gracefully?

Yes. The generated decoder functions in [`internal/ast/decoder_generated.go`](https://github.com/microsoft/typescript-go/blob/main/internal/ast/decoder_generated.go) return explicit `error` values created with `fmt.Errorf` when encountering unknown node kinds. This allows callers such as LSP servers or CLI tools to log the malformed input and continue operation, unlike the panic-based handling in Update methods which terminates the process.

### Where are the generated error handling patterns located?

The error handling implementations reside in three generated files under `internal/ast/`: [`ast_generated.go`](https://github.com/microsoft/typescript-go/blob/main/ast_generated.go) contains panic guards in Update methods, [`decoder_generated.go`](https://github.com/microsoft/typescript-go/blob/main/decoder_generated.go) contains error-returning decoder functions, and [`kind_generated.go`](https://github.com/microsoft/typescript-go/blob/main/kind_generated.go) provides the Kind enum used in panic messages. These files are produced by [`_scripts/generate-go-ast.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-go-ast.ts) and [`_scripts/generate-encoder.ts`](https://github.com/microsoft/typescript-go/blob/main/_scripts/generate-encoder.ts) during the build process.