# How to Troubleshoot SWC Parsing Errors for TypeScript and Modern JavaScript Syntax

> Troubleshoot SWC parsing errors for TypeScript and JavaScript. Learn to examine spans, drain lexer diagnostics, and trace execution to fix issues efficiently.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: how-to-guide
- Published: 2026-06-15

---

**Troubleshoot SWC parsing errors by examining the error's `Span`, draining lexer diagnostics with `take_errors()`, and tracing the execution path through `Parser::parse_program` to determine whether the failure originates in the lexer, TypeScript-specific grammar rules, or contextual validation.**

When working with the swc-project/swc compiler, parsing errors in TypeScript or modern ECMAScript syntax typically surface during the lexing or AST construction phases. Understanding how to troubleshoot SWC parsing errors requires familiarity with the parser's architecture, from the `Lexer` tokenization to the grammar validation logic in `swc_ecma_parser`. This guide walks through the diagnostic workflow, error emission paths, and resolution strategies using the actual implementation details from the source code.

## Understanding the SWC Parsing Architecture

SWC processes source code by feeding it into a **lexer** (`swc_ecma_lexer`) that produces tokens, which the **parser** (`swc_ecma_parser`) consumes to build an Abstract Syntax Tree (AST). When a parsing error occurs, the `Error` type in `crate::error::Error` captures the diagnostic along with a `Span` indicating the exact source location.

### Entry Points and Syntax Detection

The parsing flow begins at `Parser::parse_program` in [`crates/swc_ecma_parser/src/parser/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/mod.rs) (lines 89‑125). This method determines whether the source represents a **module** or a **script**, then dispatches to either `parse_module_item_block_body` or `parse_stmt_block_body`.

TypeScript-specific branches are guarded by `#[cfg(feature = "typescript")]` and runtime checks like `self.syntax().typescript()`. For TypeScript modules, the entry point is `Parser::parse_typescript_module` at lines 61‑71. All token consumption flows through the `Buffer` wrapper (`self.input`), using helper methods such as `bump()`, `expect()`, and `eat_ident_ref()` to advance or validate tokens.

### Error Collection and Emission

Errors propagate through two primary paths:

- **Early-error checks** for reserved words, illegal assignments, or strict-mode violations call `emit_err` or `emit_strict_mode_err` (defined at lines 61‑68 of [`mod.rs`](https://github.com/swc-project/swc/blob/main/mod.rs)).
- **Token-level errors** occur when the lexer returns `Token::Error`, which the parser handles via `expect_error_token_and_bump` (lines 70‑80).

All collected errors reside in the lexer's `ErrorCollector`. After parsing, calling `parser.take_errors()` drains any lexer-side diagnostics, while the main parse result returns `Result<Program, Error>`.

## Common Causes of Parsing Failures

SWC parsing errors generally fall into five categories based on where the validation occurs:

- **Unexpected tokens** — Triggered when `Parser::expect` encounters a token that violates grammar expectations (e.g., missing semicolons or stray braces). The `unexpected!` macro generates these errors throughout the parser.
- **Reserved word misuse** — Occurs when identifiers like `eval`, `arguments`, or `await` (in strict mode) appear as binding targets. The check lives in `Parser::check_assign_target` and `Expr::is_valid_simple_assignment_target`.
- **TypeScript-only syntax** — JSX type casts, `as` assertions, and type-only imports require `self.syntax().typescript()` to return true. These guards appear in `parse_type`, `parse_prop_name`, and the dedicated `typescript` module at [`crates/swc_ecma_parser/src/parser/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/typescript.rs).
- **Strict-mode violations** — Duplicate `let`/`const` declarations, use of `with`, or `delete` on unqualified identifiers trigger `Parser::emit_strict_mode_err`.
- **Lexical errors** — Unterminated strings or illegal Unicode escapes originate in [`crates/swc_ecma_lexer/src/parser/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_lexer/src/parser/mod.rs) and propagate as `Token::Error`.

## Step-by-Step Troubleshooting Workflow

Follow this systematic approach to diagnose and resolve parsing failures:

1. **Capture both lexer and parser errors**

   Always drain the error collector after a failed parse to see lexer-level diagnostics that might precede the parser error:

   ```rust
   use swc_ecma_parser::{Parser, StringInput, Syntax, EsVersion};
   use swc_common::{SourceMap, FileName};

   let cm = SourceMap::default();
   let fm = cm.new_source_file(FileName::Custom("input.ts".into()), src.into());

   let lexer = swc_ecma_parser::lexer::Lexer::new(
       Syntax::typescript(),
       EsVersion::Es2022,
       StringInput::from(&*fm),
       None,
   );
   let mut parser = Parser::new_from(lexer);

   match parser.parse_program() {
       Ok(program) => println!("Parsed successfully"),
       Err(err) => {
           eprintln!("Parser error: {}", err);
           // Critical: flush lexer errors
           for lex_err in parser.take_errors() {
               eprintln!("Lexer error: {}", lex_err);
           }
       }
   }
   ```

2. **Inspect the error's `Span`**

   The `Error` type contains a `Span` pointing to the exact byte range. Use the `SourceMap` to extract the problematic snippet:

   ```rust
   if let Ok(snippet) = cm.span_to_snippet(err.span()) {
       eprintln!("Error at {:?}: {}", err.span(), snippet);
   }
   ```

3. **Verify the parsing context**

   Many errors depend on context flags (module vs. script, strict mode, `InType`). Dump the current context before the failing rule:

   ```rust
   eprintln!("Context: {:?}", parser.ctx());
   ```

4. **Enable debug tracing**

   Compile SWC with `--features debug` to activate `trace_cur!` macros. This outputs every token processed and the parser rule being applied, helping you pinpoint where the grammar diverges from your expectations.

5. **Isolate the construct**

   Reduce the source to the smallest fragment that still triggers the error. This determines whether the bug lies in:
   
   - The lexer (`crates/swc_ecma_lexer/src/parser/*`)
   - TypeScript-specific logic ([`crates/swc_ecma_parser/src/parser/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/typescript.rs))
   - General expression parsing ([`crates/swc_ecma_parser/src/parser/expr.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/expr.rs))

6. **Map error codes to implementation**

   SWC uses TypeScript-compatible error codes. Search the codebase for the specific code:
   
   - `TS1003` (duplicate export name) — emitted in `Parser::record_exported_name`
   - `TS2406` (invalid assignment target) — emitted in `Parser::check_assign_target`

7. **Validate against the test suite**

   Run the parser's fixture tests to verify behavior against known-good patterns:

   ```bash
   cd crates/swc_ecma_parser
   cargo test --features typescript
   ```

## Resolving Common Parsing Gotchas

| Symptom | Likely Cause | Resolution |
|---------|--------------|------------|
| **"Unexpected token `}`"** at EOF | Unclosed block or class body | Verify all opening `{` have matching `}`; use `cargo fmt` to auto-correct indentation. |
| **"Expected `;` but found `}`"** | Automatic semicolon insertion (ASI) failed due to a line-break before `}` | Insert an explicit semicolon or restructure the statement to avoid the newline. |
| **"TS2406: Assignment to const variable"** | Attempting to reassign a `const` or `readonly` binding | Change the declaration to `let` or remove the reassignment. |
| **"TS1003: Duplicate export name"** | Two exports share the same identifier | Rename one export or consolidate the declarations. |
| **"Invalid Unicode escape"** | `\u{...}` sequence contains an out-of-range code point | Ensure the escape represents a valid Unicode scalar value (0‑10FFFF). |

## Essential Source Files for Debugging

When tracing a parsing error, reference these key locations in the swc-project/swc repository:

- **[`crates/swc_ecma_parser/src/parser/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/mod.rs)** — Core parsing driver containing `Parser::parse_program`, context handling, and `emit_err`.
- **[`crates/swc_ecma_parser/src/parser/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/typescript.rs)** — TypeScript-specific productions including interfaces, type aliases, and enums.
- **[`crates/swc_ecma_parser/src/parser/util.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/util.rs)** — Helper methods like `eat_ident_ref` and `is_ident_ref` for token validation.
- **[`crates/swc_ecma_lexer/src/parser/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_lexer/src/parser/mod.rs)** — Lexical analysis implementation where `Token::Error` originates.
- **[`crates/swc_ecma_parser/src/error.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/error.rs)** — Definition of `SyntaxError` codes (e.g., `TS1003`, `TS2406`).
- **[`crates/swc_ecma_parser/tests/tests.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/tests/tests.rs)** — Fixture test harness for reproducing edge cases.

## Summary

- **Drain all diagnostics** using `parser.take_errors()` to capture lexer errors alongside parser errors.
- **Locate the source** via `Span` inspection and `SourceMap::span_to_snippet` to identify the exact problematic code.
- **Check context flags** (`parser.ctx()`) when errors involve strict mode or TypeScript-specific syntax.
- **Enable debug features** (`--features debug`) for token-level tracing through the parser.
- **Reference error codes** like `TS2406` and `TS1003` to map diagnostics to specific validation logic in [`mod.rs`](https://github.com/swc-project/swc/blob/main/mod.rs) and [`typescript.rs`](https://github.com/swc-project/swc/blob/main/typescript.rs).
- **Isolate the failure** to determine if it stems from tokenization, TypeScript grammar, or ECMAScript validation rules.

## Frequently Asked Questions

### How do I distinguish between lexer errors and parser errors in SWC?

Lexer errors occur during tokenization (e.g., unterminated strings) and are stored separately from parser errors. After parsing, call `parser.take_errors()` to retrieve any lexer diagnostics. Parser errors (e.g., unexpected tokens) are returned directly in the `Result` from methods like `parse_program()`.

### What does error code TS2406 indicate in SWC?

Error `TS2406` signifies an invalid left-hand side in an assignment expression. According to the source in [`crates/swc_ecma_parser/src/parser/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/mod.rs), this is emitted by `Parser::check_assign_target` when code attempts to assign to a `const` binding or an expression that is not a valid simple assignment target (e.g., a literal or function call).

### How can I enable verbose tracing to debug parser state?

Compile SWC with the `debug` feature flag: `cargo build --features debug`. This activates `trace_cur!` macros throughout `swc_ecma_parser` that print every token consumed and the current parsing rule to stdout, allowing you to trace exactly where the parser rejects your syntax.

### Where does SWC implement TypeScript-specific parsing logic?

TypeScript extensions are implemented in [`crates/swc_ecma_parser/src/parser/typescript.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/src/parser/typescript.rs). This module handles type annotations, interfaces, enums, and namespace declarations. The parser checks `self.syntax().typescript()` before entering these code paths, ensuring the features are only active when TypeScript support is enabled.