swc-project/swc · error
illegal conversion: Cannot convert {:?} to AssignmentPattern
Error message
illegal conversion: Cannot convert {:?} to AssignmentPatternLeft What it means
The left side of a default value (`AssignPat`, e.g. `a = 1` inside destructuring) maps to Babel's AssignmentPatternLeft, which accepts Id/Array/Object/Member only. A `PatOutput::Expr` that is not a member expression — e.g. `(a || b) = init` — has no legal ESTree shape, so this branch of the inner match panics.
Source
Thrown at crates/swc_estree_compat/src/babelify/pat.rs:107
PatOutput::Id(i) => PatternLike::Id(i),
PatOutput::Array(a) => PatternLike::ArrayPat(a),
PatOutput::Rest(r) => PatternLike::RestEl(r),
PatOutput::Object(o) => PatternLike::ObjectPat(o),
PatOutput::Assign(a) => PatternLike::AssignmentPat(a),
PatOutput::Expr(_) => panic!("illegal conversion: Cannot convert {:?} to LVal", &pat),
}
}
}
impl From<PatOutput> for AssignmentPatternLeft {
fn from(pat: PatOutput) -> Self {
match pat {
PatOutput::Id(i) => AssignmentPatternLeft::Id(i),
PatOutput::Array(a) => AssignmentPatternLeft::Array(a),
PatOutput::Object(o) => AssignmentPatternLeft::Object(o),
PatOutput::Expr(expr) => match *expr {
Expression::Member(e) => AssignmentPatternLeft::Member(e),
_ => panic!(
"illegal conversion: Cannot convert {:?} to AssignmentPatternLeft",
&expr
),
},
PatOutput::Rest(_) => panic!(
"illegal conversion: Cannot convert {:?} to AssignmentPatternLeft",
&pat
),
PatOutput::Assign(_) => panic!(
"illegal conversion: Cannot convert {:?} to AssignmentPatternLeft",
&pat
),
}
}
}
impl From<PatOutput> for Param {
fn from(pat: PatOutput) -> Self {View on GitHub (pinned to 5176682b65)
Solutions
- Validate that AssignPat.left, when it is Pat::Expr, wraps a MemberExpr before babelify
- Fix the malformed default-value syntax in the source
- In transforms, only attach defaults to Id/Array/Object/Member targets
- Return a diagnostic instead of converting when validation fails
Example fix
// before
let estree = module.babelify(); // panics on `[(a + b) = 2] = xs`
// after
if module.has_invalid_default_targets() {
return Err(anyhow!("invalid default-value target"));
}
let estree = module.babelify(); Defensive patterns
Strategy: type-guard
Validate before calling
use swc_ecma_ast::{AssignPat, Expr, Pat};
fn default_target_ok(left: &Pat) -> bool {
match left {
Pat::Ident(_) | Pat::Array(_) | Pat::Object(_) => true,
Pat::Expr(e) => matches!(&**e, Expr::Member(_)),
_ => false,
}
}
fn defaults_ok(p: &AssignPat) -> bool { default_target_ok(&p.left) } Type guard
fn assign_pat_left_ok(p: &AssignPat) -> bool {
match &*p.left {
Pat::Ident(_) | Pat::Array(_) | Pat::Object(_) => true,
Pat::Expr(e) => matches!(&**e, Expr::Member(_)),
_ => false,
}
} Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
catch_unwind(AssertUnwindSafe(|| program.babelify()))
.map_err(|_| anyhow!("invalid default-value target"))?; Prevention
- Attach defaults only to Id/Array/Object/Member targets in generated ASTs
- Validate nested destructuring before conversion when input came from recovery
- Add tests with `[(a + b) = 2] = xs` to confirm your tool rejects it
When it happens
Trigger: Recovered or synthetic destructuring where an expression pattern wraps a non-member expression in a default-value slot: `[(a + b) = 2] = xs`, `({ x: (f()) = 0 } = o)`; converted via `left: self.left.babelify(ctx).into()` at pat.rs:235.
Common situations: Error-recovered parses of typo'd defaults; codemods generating defaults programmatically; ASTs hand-built for tests.
Related errors
- illegal conversion: Cannot convert {:?} to PatOutput
- illegal conversion: Cannot convert {:?} to Pattern
- illegal conversion: Cannot convert {:?} to LVal
- Cannot convert {p:?} to Param
- illegal conversion: Cannot convert {:?} to ObjectMember
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/b4baf577e82af1ba.
Report an issue: GitHub.