swc-project/swc · error
internal error: entered unreachable code
Error message
internal error: entered unreachable code
What it means
When swc_bundler finalizes a bundle whose output format is IIFE, may_wrap_with_iife rewrites `export <decl>` statements into properties on the returned object. It converts Class, Fn and Var declarations; any other Decl — TsEnum, TsInterface, TsTypeAlias, TsModule, i.e. TypeScript-only syntax that should have been stripped — hits unreachable!().
Source
Thrown at crates/swc_bundler/src/bundler/finalize.rs:196
ModuleDecl::ExportDecl(export) => {
match &export.decl {
Decl::Class(ClassDecl { ident, .. })
| Decl::Fn(FnDecl { ident, .. }) => {
props.push(PropOrSpread::Prop(Box::new(Prop::Shorthand(
ident.clone(),
))));
}
Decl::Var(decl) => {
let ids: Vec<Ident> = find_pat_ids(decl);
props.extend(
ids.into_iter()
.map(Prop::Shorthand)
.map(Box::new)
.map(PropOrSpread::Prop),
);
}
_ => unreachable!(),
}
Some(export.decl.into())
}
ModuleDecl::ExportNamed(NamedExport {
specifiers,
src: None,
..
}) => {
for s in specifiers {
match s {
ExportSpecifier::Namespace(..) => {
// unreachable
}
ExportSpecifier::Default(s) => {
props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {View on GitHub (pinned to 5176682b65)
Solutions
- Run the TypeScript strip/compile pass before the bundler so only plain ESM reaches finalize
- Verify pass ordering in your pipeline: typescript::strip must precede bundling
- If TypeScript was already stripped, capture the failing module and report it to swc as a bundler gap
Example fix
// before (raw TS fed to bundler)
export enum Flag { A, B }
// after (pre-stripped by typescript::strip before bundling)
export var Flag;
(function (Flag) {
Flag[Flag["A"] = 0] = "A";
Flag[Flag["B"] = 1] = "B";
})(Flag || (Flag = {})); Defensive patterns
Strategy: validation
Validate before calling
// Detect TS-only exported declarations before bundling with IIFE output
let ts_export = regex::Regex::new(
r#"(?m)^\s*export\s+(?:enum|interface|type|namespace|module|declare)\b"#,
).unwrap();
for (path, src) in &sources {
if ts_export.is_match(src) {
return Err(format!("{} still contains TypeScript exports; strip first", path));
}
} Try / catch
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| bundler.bundle(&entries))) {
Ok(v) => v?,
Err(p) => {
if panic_message(&p).contains("entered unreachable code") && cfg_module_iife {
// retry after inserting typescript::strip at the front of the pipeline
} else { std::panic::resume_unwind(p); }
}
} Prevention
- Order the pipeline: typescript::strip -> bundler (never the reverse)
- When output ModuleType is IIFE, run fixture tests over TS-heavy entry files
- Keep a lint rule forbidding `export enum/interface/type/namespace` in bundler inputs
When it happens
Trigger: Bundling with ModuleType::Iife where an entry still contains TypeScript exports such as `export enum E {}`, `export interface I {}`, `export type T = ...`, or `export namespace N {}` because the TypeScript strip pass never ran (or ran after the bundler).
Common situations: Custom swc pipelines invoking the bundler on raw TypeScript; a pass chain where typescript::strip was accidentally omitted or ordered after bundling; plugins re-inserting TS nodes into the graph.
Related errors
- Decl in ExportDecl: {:?}
- Duplicated element (${elements[j].key})
- Method visitTsType not implemented.
- illegal conversion: Cannot convert {:?} to ModuleDeclaration
- unable to access unknown nodes
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/ca0825bd0076a43f.
Report an issue: GitHub.