swc-project/swc · error
illegal conversion: Cannot convert {:?} to Expression - babe
Error message
illegal conversion: Cannot convert {:?} to Expression - babel has no equivalent What it means
babelify of expressions returns ExprOutput — either a normal Expression or a PrivateName (`#foo`). Babel's Expression union has no PrivateName member, so `From<ExprOutput> for Expression` (expr.rs:180-189) panics when a PrivateName lands in a slot typed as a plain Expression (object property values, call arguments, etc.). Babel only permits private names in two expression contexts: the left side of `#x in obj` and member properties like `this.#x`.
Source
Thrown at crates/swc_estree_compat/src/babelify/expr.rs:184
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 {
match o {
ExprOutput::Expr(e) => BinaryExprLeft::Expr(e),
ExprOutput::Private(p) => BinaryExprLeft::Private(p),
}
}
}
impl From<ExprOutput> for ObjectKey {
fn from(o: ExprOutput) -> Self {View on GitHub (pinned to 5176682b65)
Solutions
- Keep private names only in `#x in obj` left-side or member-property positions.
- Fix the producer (plugin/builder) that placed a PrivateName into a plain expression slot.
- Pre-scan the AST for PrivateName in Expression-typed positions and reject with a span.
Example fix
// before: private name in a value slot (invalid)
Prop::KeyValue(KeyValueProp { key: k, value: Expr::PrivateName(p) })
// after: keep it in a supported position
Expr::Bin(BinExpr { op: op!(in), left: Box::new(Expr::PrivateName(p)), right, .. }) Defensive patterns
Strategy: type-guard
Type guard
// PrivateName is only legal as `#x in obj` left side or member property.
fn has_private_name_in_value(program: &Program) -> bool {
struct Scan(bool);
impl Visit for Scan {
fn visit_expr(&mut self, e: &Expr) {
if let Expr::PrivateName(_) = e {
// reaching generic expression traversal at all means it is
// outside the supported positions
self.0 = true;
}
e.visit_children_with(self);
}
fn visit_member_expr(&mut self, m: &MemberExpr) {
m.obj.visit_with(self); // PrivateName member props handled elsewhere
}
}
let mut s = Scan(false);
program.visit_with(&mut s);
s.0
} Prevention
- Keep `#x` usages limited to `#x in obj` and `this.#x` member accesses.
- In plugins, never emit Expr::PrivateName into argument/value slots.
- Test private-member code paths through the converter explicitly.
When it happens
Trigger: Babelifying an AST where a PrivateName appears as a general expression value — practically only from plugins/codemods or hand-built ASTs, since the parser rejects `#x` in value positions (BinaryExprLeft, in contrast, is converted losslessly at expr.rs:192+).
Common situations: Codemods moving private names around; AST fixtures exercising private class members.
Related errors
- illegal conversion: Cannot convert {:?} to ObjectKey - babel
- illegal conversion: Cannot convert {:?} to ClassBodyEl
- illegal conversion: Cannot convert {:?} to ExprOutput
- illegal conversion: Cannot convert {:?} to ExprOutput - babe
- unable to access unknown nodes
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/9db8edeeade969b7.
Report an issue: GitHub.