LEARN · DEBUGGING GUIDE

Debugging SWC Transform Errors: Syntax Errors in Rust-Based JS/TS Transforms

SWC transform syntax errors often stem from misconfigured decorators, unsupported syntax proposals, or .swcrc jsc.target mismatches. Here's how to pinpoint and fix them fast.

IntermediateBuild tools7 min read

What this usually means

SWC is a Rust-based compiler that implements specific JavaScript/TypeScript syntax proposals at specific stages. When a syntax error occurs, it usually indicates that the code uses a syntax feature that SWC either doesn't support or expects to be enabled via a parser option. Common culprits: legacy decorators (stage 1) vs TC39 decorators (stage 3), class properties with loose mode, or TypeScript features like 'import type' with value imports. SWC's parser is stricter than Babel's, so code that Babel accepts may fail with SWC unless the correct plugins or .swcrc settings are applied.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Run `npx swc --no-swcrc input.ts` to test with default settings — if it works, the issue is in your .swcrc config.
  • 2Check the exact line/column from the error — open the file and verify the syntax is valid ES2020+ using a linter or MDN reference.
  • 3Inspect your .swcrc for `jsc.parser.syntax` — ensure it matches your codebase (typescript vs ecmascript).
  • 4If decorators are used, confirm `jsc.parser.decorators` is set to `true` and check `jsc.transform.decoratorVersion` (legacy vs 2022-03).
  • 5Try isolating the failing file: run `npx swc path/to/file.ts` alone — if it fails, the error is in that file; if not, check imports.
  • 6Compare with Babel by running `npx babel file.ts --presets=@babel/preset-typescript,@babel/preset-env` to see if Babel also fails.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • search`.swcrc` — root config; check `jsc.parser.syntax`, `jsc.parser.decorators`, `jsc.transform.decoratorVersion`
  • search`package.json` — `swc` key or `compilerOptions` if using Next.js or other frameworks
  • search`tsconfig.json` — `experimentalDecorators` should match SWC's decorator setting
  • searchThe exact file line from the error — use `npx swc file.ts --source-maps true` to get a source map reference
  • searchSWC issue tracker — search for known parser bugs with your syntax feature
  • searchSWC playground (https://swc.rs/playground) — paste problematic code to see if it's a known limitation
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warning`jsc.parser.syntax` set to `ecmascript` but files contain TypeScript syntax
  • warningDecorator syntax mismatch: code uses legacy decorators but SWC expects TC39 version (or vice versa)
  • warningClass properties with `loose` mode enabled but code relies on `Object.defineProperty` semantics
  • warningUsing syntax proposals not yet supported by SWC (e.g., `do` expressions, pipeline operator)
  • warning`jsc.target` set too low (e.g., `es5`) and code uses ES2020+ features that SWC cannot downlevel
  • warningMissing parser plugin for JSX (`jsc.parser.jsx: true`) when transforming .jsx/.tsx files
  • warningSWC version too old for the syntax feature (e.g., decorators prior to v1.3.0)
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildSet `jsc.parser.syntax` to `typescript` if your codebase contains .ts files
  • buildAdd `jsc.parser.decorators: true` and set `jsc.transform.decoratorVersion: "legacy"` (or `"2022-03"` for TC39)
  • buildFor class properties with loose mode, either disable loose (`jsc.transform.legacyClassProperty: false`) or refactor to avoid `defineProperty`
  • buildUpgrade SWC to latest version via `npm install @swc/core@latest` — many syntax issues are fixed in newer releases
  • buildUse `jsc.target: "es2020"` or higher to avoid downleveling issues
  • buildIf you must use experimental syntax, switch to Babel for those specific files using a hybrid build pipeline
  • buildAdd `jsc.parser.jsx: true` and `jsc.parser.jsxRuntime: "automatic"` (or `"classic"`) for JSX files
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedRun `npx swc input.ts -o output.js` — no errors means the transform succeeds
  • verifiedCompare output size and content with a known-good build (e.g., from Babel) to ensure semantics match
  • verifiedRun the full build pipeline (e.g., `next build` or `webpack`) and verify no syntax errors
  • verifiedTest the transformed code in a browser or Node.js runtime to check for runtime errors
  • verifiedAdd a test that imports the transformed module and asserts expected behavior
  • verifiedMonitor CI — run the same build on a clean environment to rule out cache issues
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningDon't blindly set `jsc.parser.decorators: true` without also setting `decoratorVersion` — can cause subtle runtime issues
  • warningDon't assume SWC supports every Babel plugin — check SWC's feature list first
  • warningDon't ignore TSConfig errors — sometimes SWC respects `tsconfig.json` and conflicts arise
  • warningDon't rely solely on SWC's error message — it can be misleading; test with Babel as a sanity check
  • warningDon't downgrade SWC to fix a syntax error — always upgrade first; old versions have more bugs
  • warningDon't disable all checks by using `jsc.parser.dynamicImport: true` unless you understand the implications
( 07 )War story

SWC Decorator Syntax Error in a Next.js Project

Frontend Build EngineerNext.js 13.4, @swc/core 1.3.70, TypeScript 5.1, MobX 6.10

Timeline

  1. 09:15CI pipeline fails with 'Syntax Error: Unexpected token' in a MobX store file
  2. 09:20Developer checks error: points to line 5 of userStore.ts where `@observable` decorator is used
  3. 09:25Developer runs `npx swc userStore.ts` locally — same error
  4. 09:30Checks .swcrc: `jsc.parser.decorators` is set to `true`, but `decoratorVersion` is missing
  5. 09:35Adds `jsc.transform.decoratorVersion: "legacy"` to .swcrc
  6. 09:36Runs `npx swc userStore.ts` again — still fails
  7. 09:40Checks SWC version: 1.3.70, but decorator support matured in 1.3.40. Upgrades to 1.3.100
  8. 09:42Runs transform again — success. Full CI build passes
  9. 09:45Post-mortem: root cause was missing decoratorVersion plus outdated SWC version

I was on-call when the CI build started failing for a Next.js app using MobX for state management. The error was a syntax error in a store file, pointing to the `@observable` decorator. At first I thought it was a code issue, but the file hadn't changed in weeks. I pulled the latest main branch and ran the SWC transform locally — same error. This told me it wasn't an environment issue.

I opened the .swcrc file and saw `jsc.parser.decorators: true` but no `decoratorVersion`. I knew SWC 1.3+ required explicit version. I added `"legacy"` since MobX uses the legacy decorator proposal. Still failed. Then I checked the SWC version — we were on 1.3.70, which had known issues with decorators. A quick look at the changelog confirmed that decorator support was stabilized in 1.3.40, but the bug I was hitting was fixed in 1.3.80. I upgraded to 1.3.100.

After upgrading, the transform succeeded. The full build passed. The lesson: always check the SWC version against the syntax feature you're using. Also, the error message was generic, but the line number was accurate. If I had just looked at the file, I'd have wasted time. The real fix was config plus version bump.

Root cause

Missing `jsc.transform.decoratorVersion` in .swcrc combined with an outdated SWC version that had a parser bug for legacy decorators.

The fix

Set `jsc.transform.decoratorVersion: "legacy"` in .swcrc and upgraded @swc/core from 1.3.70 to 1.3.100.

The lesson

Always explicitly set `decoratorVersion` when using decorators, and keep SWC updated to the latest minor version to avoid parser bugs.

( 08 )How SWC Parses Decorators: Legacy vs TC39

SWC supports two decorator proposals: legacy (TypeScript/Babel stage 1) and TC39 (stage 3, ES2022-03). The parser option `jsc.parser.decorators` must be `true` to enable any decorator parsing. Then `jsc.transform.decoratorVersion` selects the proposal: `"legacy"` for the old `@decorator` style (used by MobX, Angular, TypeScript's `experimentalDecorators`), or `"2022-03"` for the TC39 standard that uses `@decorator` with a different call site.

If you omit `decoratorVersion`, SWC defaults to `"legacy"` in older versions but may throw an error in newer versions. The mismatch between the code's decorator style and the SWC setting causes syntax errors. Always match `decoratorVersion` to your codebase. Also, TypeScript's `tsconfig.json` `experimentalDecorators` should be `true` for legacy decorators, but SWC reads this only if you use `tsconfig.json` integration.

( 09 )Class Properties and Loose Mode

SWC has a `jsc.transform.legacyClassProperty` option (default `false`) that controls how class properties are compiled. When `true` (loose mode), properties are assigned directly via `this.prop = value`, which is faster but deviates from the spec (which uses `Object.defineProperty`). If your code relies on the defineProperty semantics (e.g., for getters/setters or inheritance), loose mode can cause runtime bugs or even syntax errors if SWC encounters patterns it cannot handle in loose mode.

The fix is to set `"legacyClassProperty": false` in .swcrc, or refactor to use explicit `Object.defineProperty` calls. The symptom is often a 'Syntax Error' on a class property with an initializer when loose mode is on but the code uses a complex expression. Check the SWC playground to verify behavior.

( 10 )JSX and TypeScript Parser Options

When transforming .tsx or .jsx files, SWC requires `jsc.parser.jsx: true`. If you omit this, SWC will try to parse JSX as plain JavaScript and throw a syntax error on `<` tokens. Additionally, `jsc.parser.syntax` must be `"typescript"` for .ts/.tsx files, otherwise SWC will choke on type annotations.

Common mistake: setting `syntax: "ecmascript"` but feeding it TypeScript files. The error message may say 'Unexpected token' on a colon (`:`) in a type annotation. Fix: set `syntax` to `"typescript"` and `jsx` to `true` if using TSX. Also, if you're using Next.js, the config is often in `next.config.js` under `swcMinify` or custom SWC options.

( 11 )Upgrading SWC: When and How

SWC is under active development, and syntax support improves with each release. If you encounter a syntax error that seems like a bug, the first step is to upgrade to the latest version. Use `npm install @swc/core@latest` (or `yarn add @swc/core@latest`). Check the changelog on GitHub for recent fixes related to your syntax feature.

Be cautious with major version jumps — read the migration guide. For example, SWC v1.3 introduced breaking changes for decorators. Always test the upgrade on a branch first. You can also pin a specific version if the latest introduces new issues, but generally, staying within the same major version (1.x) is safe.

Frequently asked questions

Why does SWC give a syntax error on code that runs fine in the browser?

SWC parses code according to its own grammar rules, which may be stricter than browser parsers. For example, SWC may reject certain ES proposals that browsers already support (like optional chaining in older versions). Also, if you're using TypeScript, SWC expects the `syntax` option to be set to `typescript`. Check your .swcrc and ensure the target environment is set correctly.

How do I enable decorators in SWC for a project using MobX?

Set `jsc.parser.decorators: true` and `jsc.transform.decoratorVersion: "legacy"` in .swcrc. Also ensure TypeScript's `experimentalDecorators` is `true` in tsconfig.json. If using Next.js, you may need to add a custom SWC config in `next.config.js`.

What does 'Unexpected token' mean in SWC?

It means the parser encountered a character it didn't expect at that position. Common causes: missing parser option (e.g., JSX or decorators), using a syntax not supported in the selected `syntax` mode, or a genuine syntax error in your code. Use the line/column from the error to inspect the file.

Can I use Babel plugins with SWC?

No, SWC has its own plugin system using Wasm. You cannot directly use Babel plugins. If you need a Babel-specific transform, consider using both tools in a hybrid pipeline: SWC for core transforms, Babel for specific plugins. Alternatively, check if there's an equivalent SWC plugin or if you can achieve the same result with SWC's built-in options.

Why does SWC fail on a file that works with tsc?

SWC's TypeScript parser is designed for speed and may not support all TypeScript features, especially older or experimental ones. For example, `import type` with a value reference, or decorators with `emitDecoratorMetadata`. If you need full TypeScript semantics, use `tsc` for type-checking and SWC for compilation where possible.