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

  1. Disable the treat_err_as_bug Handler flag if it is set; it is intended for compiler development, not production
  2. Upgrade swc_core/swc_ecma_parser to the latest patch release where the internal bug is likely fixed
  3. Minimize the failing input and file an SWC issue with the bug-level diagnostic and span
  4. 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

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


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/90899f91d773d6fc. Report an issue: GitHub.