swc-project/swc · error

unable to access unknown nodes

Error message

unable to access unknown nodes

What it means

swc_ecma_ast can be built with `--cfg=swc_ast_unknown`, which adds `Unknown` variants to AST enums for forward compatibility with plugin hosts (see crates/swc_plugin_backend_tests/tests/fixture/.cargo/config.toml). Under that cfg, every exhaustive match in swc_estree_compat carries an `_ => panic!("unable to access unknown nodes")` arm (expr.rs:175 for Expr); it fires when the AST actually contains an Unknown node, i.e. the AST came from a different swc_ecma_ast version than the converter was compiled against.

Source

Thrown at crates/swc_estree_compat/src/babelify/expr.rs:175

            ),
            Expr::TsConstAssertion(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::TsSatisfies(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::OptChain(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            Expr::Invalid(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ExprOutput - babel has no equivalent",
                &self
            ),
            #[cfg(swc_ast_unknown)]
            _ => panic!("unable to access unknown nodes"),
        }
    }
}

impl From<ExprOutput> for Expression {
    fn from(o: ExprOutput) -> Self {
        match o {
            ExprOutput::Expr(expr) => *expr,
            ExprOutput::Private(_) => panic!(
                "illegal conversion: Cannot convert {:?} to Expression - babel has no equivalent",
                &o
            ),
        }
    }
}

impl From<ExprOutput> for BinaryExprLeft {
    fn from(o: ExprOutput) -> Self {

View on GitHub (pinned to 5176682b65)

Solutions

  1. For normal (non-plugin) builds, drop `--cfg=swc_ast_unknown` from RUSTFLAGS so the arm is compiled out.
  2. Align swc_ecma_ast / swc_core versions between the AST producer and swc_estree_compat so no Unknown nodes exist.
  3. If you must run under the cfg, reject Unknown nodes at your boundary before calling babelify.

Example fix

# before: .cargo/config.toml
[build]
rustflags = ["--cfg=swc_ast_unknown"]

# after
[build]
rustflags = []
Defensive patterns

Strategy: validation

Validate before calling

// When running under --cfg=swc_ast_unknown, screen deserialized ASTs first.
#[cfg(swc_ast_unknown)]
fn has_unknown_expr(program: &Program) -> bool {
    struct Scan(bool);
    impl Visit for Scan {
        fn visit_expr(&mut self, e: &Expr) {
            if matches!(e, Expr::Unknown(_)) { self.0 = true; }
            e.visit_children_with(self);
        }
    }
    let mut s = Scan(false);
    program.visit_with(&mut s);
    s.0
}

Prevention

When it happens

Trigger: Building swc crates with rustflags = ["--cfg=swc_ast_unknown"] and babelifying an AST containing Unknown variants — typically deserialized from a plugin/host process whose swc_ecma_ast/swc_core version differs from swc_estree_compat's.

Common situations: SWC plugin development where plugin and host crate versions drift; CI configurations enabling the cfg globally; mixing published swc crate versions in one dependency graph.

Related errors


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