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

  1. Validate that AssignPat.left, when it is Pat::Expr, wraps a MemberExpr before babelify
  2. Fix the malformed default-value syntax in the source
  3. In transforms, only attach defaults to Id/Array/Object/Member targets
  4. 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

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


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