swc-project/swc · critical
parser internal bug
Error message
parser internal bug
What it means
Handler::span_bug emits a diagnostic with Level::Bug at a given span and then panics with ExplicitBug, whose Display text is 'parser internal bug'. It marks an invariant violation: the parser or a transform reached a state its authors considered impossible. It is also reached indirectly via delay_span_bug when HandlerFlags::treat_err_as_bug is enabled.
Source
Thrown at crates/swc_common/src/errors/mod.rs:662
result.set_span(sp);
result
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.emit_with_code(&sp.into(), msg, code, Error);
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Warning);
}
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.emit_with_code(&sp.into(), msg, code, Warning);
}
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.emit(&sp.into(), msg, Bug);
panic!("{}", ExplicitBug);
}
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
if self.flags.treat_err_as_bug {
// FIXME: don't abort here if report_delayed_bugs is off
self.span_bug(sp, msg);
}
let mut diagnostic = Diagnostic::new(Level::Bug, msg);
diagnostic.set_span(sp.into());
self.delay_as_bug(diagnostic);
}
fn delay_as_bug(&self, diagnostic: Diagnostic) {
if self.flags.report_delayed_bugs {
DiagnosticBuilder::new_diagnostic(self, diagnostic.clone()).emit();
}
self.delayed_span_bugs.borrow_mut().push(diagnostic);
}View on GitHub (pinned to 5176682b65)
Solutions
- Disable the treat_err_as_bug Handler flag if it is set; it is intended for compiler development, not production
- Upgrade swc_core/swc_ecma_parser to the latest patch release where the internal bug is likely fixed
- Minimize the failing input and file an SWC issue with the bug-level diagnostic and span
- Embedders: isolate compilation in std::panic::catch_unwind so one bad input cannot kill the service
Example fix
// before (debug flag leaked into production)
let handler = Handler::with_emitter_and_flags(
Box::new(emitter),
HandlerFlags { treat_err_as_bug: true, ..Default::default() },
);
// after
let handler = Handler::with_emitter_and_flags(
Box::new(emitter),
HandlerFlags::default(), // treat_err_as_bug = false
); Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: never enable compiler-dev flags in shipped builds
assert!(!handler.flags.treat_err_as_bug,
"treat_err_as_bug must stay off in production"); Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
use swc_common::errors::ExplicitBug;
let result = catch_unwind(AssertUnwindSafe(|| transform(input)));
if let Err(payload) = result {
if payload.downcast_ref::<ExplicitBug>().is_some() {
// internal bug: skip input, keep service alive, report upstream
}
} Prevention
- Keep treat_err_as_bug disabled outside compiler development
- Pin a known-good swc_core version and upgrade deliberately
- Fuzz-test inputs before feeding them to production compiles
When it happens
Trigger: Parser/transform code calls span_bug on an impossible AST state; user code enables treat_err_as_bug so delayed bugs (delay_span_bug) escalate into immediate panics; fuzzing or adversarial input trips an unhandled grammar corner.
Common situations: Fuzzing SWC inputs, copying debug/test Handler flags (treat_err_as_bug) into production builds, upgrading to an swc_core version with a parser regression on exotic syntax, plugin code calling handler.span_bug directly.
Related errors
- The requested module '{specifier}' does not provide an expor
- Cannot use import.meta outside a module
- Assignment to constant variable.
- attempted to ${action} private field on non-instance
- Class "${name}" cannot be referenced in computed property ke
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/90899f91d773d6fc.
Report an issue: GitHub.