swc-project/swc · error · Error
Unknown statement type: ${(stmt as any).type
Error message
Unknown statement type: ${(stmt as any).type What it means
Thrown by the legacy JavaScript `Visitor` base class that @swc/core exposes for legacy `plugin` callbacks. visitStatement() is a switch over a fixed enumeration of statement `type` strings (base ES statements plus TsEnum/TsInterface/TsModule/TsTypeAlias declarations); any statement type not in that list falls into `default` and throws `Unknown statement type: <type>`. The enumeration predates newer and less-common AST nodes, so walking modern syntax through a legacy plugin crashes instead of visiting it.
Source
Thrown at packages/core/src/Visitor.ts:515
case "LabeledStatement":
return this.visitLabeledStatement(stmt);
case "ReturnStatement":
return this.visitReturnStatement(stmt);
case "SwitchStatement":
return this.visitSwitchStatement(stmt);
case "ThrowStatement":
return this.visitThrowStatement(stmt);
case "TryStatement":
return this.visitTryStatement(stmt);
case "WhileStatement":
return this.visitWhileStatement(stmt);
case "WithStatement":
return this.visitWithStatement(stmt);
case "ExpressionStatement":
return this.visitExpressionStatement(stmt);
default:
throw new Error(
`Unknown statement type: ` + (stmt as any).type
);
}
}
visitSwitchStatement(stmt: SwitchStatement): Statement {
stmt.discriminant = this.visitExpression(stmt.discriminant);
stmt.cases = this.visitSwitchCases(stmt.cases);
return stmt;
}
visitSwitchCases(cases: SwitchCase[]): SwitchCase[] {
return cases.map(this.visitSwitchCase.bind(this));
}
visitSwitchCase(c: SwitchCase): SwitchCase {
c.test = this.visitOptionalExpression(c.test);
c.consequent = this.visitStatements(c.consequent);View on GitHub (pinned to 5176682b65)
Solutions
- Override visitStatement in your subclass: delegate to super.visitStatement inside try/catch and return the statement untouched when the message starts with 'Unknown statement type:'
- Or copy the switch into your override and add a `default` that handles/returns the new node type instead of throwing
- Migrate off the legacy Visitor to the current plugin/visitor API, which dispatches over the full modern AST
- Pre-transform the input (e.g. strip newer syntax) before handing the Program to the legacy plugin
Example fix
// before
class MyVisitor extends Visitor {
visitIdentifier(n) { /* ... */ return n; }
}
// -> Error: Unknown statement type: ...
// after
class MyVisitor extends Visitor {
visitStatement(stmt) {
try {
return super.visitStatement(stmt);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Unknown statement type:')) {
return stmt; // pass unrecognized statements through untouched
}
throw e;
}
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Dry-run the legacy visitor over a deep clone; if it throws on an unknown
// statement type, skip or reroute the plugin instead of failing the build.
const { Visitor } = require('@swc/core');
function legacyVisitorSupports(program) {
try {
new Visitor().visitProgram(structuredClone(program));
return true;
} catch (e) {
return !(e instanceof Error && e.message.startsWith('Unknown statement type:'));
}
} Try / catch
class SafeVisitor extends Visitor {
visitStatement(stmt) {
try {
return super.visitStatement(stmt);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Unknown statement type:')) {
return stmt; // pass through nodes this legacy visitor doesn't know
}
throw e;
}
}
} Prevention
- Prefer the current plugin/visitor API over the legacy Visitor class when handling modern syntax
- Pin the @swc/core version a legacy plugin was written against, and re-test after upgrades
- Run the plugin over your full real corpus in CI so unknown node types surface before production
When it happens
Trigger: A legacy plugin (options.plugin using `new MyVisitor().visitProgram(program)`) walking an AST that contains a statement node type absent from the switch — a node kind introduced after this visitor was written, or a hand-built/modified AST whose statement `type` strings are misspelled or non-standard.
Common situations: Reusing an old JS plugin written for plain JS with a current @swc/core on modern syntax; prototypes that synthesize AST nodes manually; upgrading @swc/core under a plugin that used to work, exposing node types the legacy switch never knew.
Related errors
- Method visitTsType not implemented.
- empty expression container
- Non-computed member expression with property other than iden
- internal error: entered unreachable code
- rest pattern should handled by array pattern handler: {:?}
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/48a46afcc1c2e42e.
Report an issue: GitHub.