swc-project/swc · error
illegal conversion: Cannot convert {:?} to BinaryExprOp
Error message
illegal conversion: Cannot convert {:?} to BinaryExprOp What it means
SWC unifies all binary and logical operators into one BinaryOp enum, but Babel splits them across two node types: BinaryExpression (arithmetic/comparison/bitwise/in/instanceof) and LogicalExpression (&&, ||, ??). BinaryOpOutput carries either a BinOp or a LogicOp, and this From impl converts it to BinaryExprOp, panicking if the value holds the logical kind. The panic means a BinExpr node was built whose operator is logical — a shape the SWC parser never produces, so it originates from programmatic AST construction or a transform.
Source
Thrown at crates/swc_estree_compat/src/babelify/operators.rs:17
use serde::{Deserialize, Serialize};
use swc_ecma_ast::{AssignOp, BinaryOp, UnaryOp, UpdateOp};
use swc_estree_ast::{BinaryExprOp, LogicalExprOp, UnaryExprOp, UpdateExprOp};
use crate::babelify::{Babelify, Context};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BinaryOpOutput {
BinOp(BinaryExprOp),
LogicOp(LogicalExprOp),
}
impl From<BinaryOpOutput> for BinaryExprOp {
fn from(o: BinaryOpOutput) -> Self {
match o {
BinaryOpOutput::BinOp(op) => op,
BinaryOpOutput::LogicOp(_) => panic!(
"illegal conversion: Cannot convert {:?} to BinaryExprOp",
&o
),
}
}
}
impl From<BinaryOpOutput> for LogicalExprOp {
fn from(o: BinaryOpOutput) -> Self {
match o {
BinaryOpOutput::LogicOp(op) => op,
BinaryOpOutput::BinOp(_) => panic!(
"illegal conversion: Cannot convert {:?} to LogicalExprOp",
&o
),
}
}
}View on GitHub (pinned to 5176682b65)
Solutions
- Use LogicalExpr (not BinExpr) when the operator is &&, ||, or ??
- If a transform rewrites operators, have it swap the node type (BinExpr <-> LogicalExpr) together with the op
- Validate before babelify: every BinExpr op must be non-logical, every LogicalExpr op must be logical
- Fall back to catch_unwind around babelify to surface a clear error
Example fix
// before
BinExpr {
op: BinaryOp::LogicalOr,
left,
right,
}
// after
LogicalExpr {
op: BinaryOp::LogicalOr,
left,
right,
} Defensive patterns
Strategy: type-guard
Validate before calling
fn bin_exprs_consistent(program: &Program) -> Result<(), String> {
for e in exprs(program) {
if let Expr::Bin(b) = e {
if matches!(b.op, BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing) {
return Err(format!("BinExpr with logical op {:?} at {:?}", b.op, b.span));
}
}
}
Ok(())
} Type guard
fn op_is_binary(o: BinaryOp) -> bool {
!matches!(o, BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing)
} Try / catch
let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| program.babelify(&ctx)))
.map_err(|_| anyhow::anyhow!("illegal conversion: logical operator used in BinaryExpression"))?; Prevention
- Never put &&, ||, or ?? in a BinExpr node
- When rewriting operators, switch node type (BinExpr <-> LogicalExpr) in the same step
- Assert op-class consistency in transform unit tests
- Prefer building expressions via helpers that choose the node type from the operator
When it happens
Trigger: Constructing a swc_ecma_ast BinExpr with op set to LogicalAnd/LogicalOr/NullishCoalescing (or mutating a BinExpr's op to a logical one) and babelifying it — the `.into()` on the babelified op hits the mismatch.
Common situations: Codemods and transforms that flip or swap operators in place; AST-building helpers/macros (e.g. swc_ecma_quote-style constructors) choosing BinExpr for `a || b`; deserializing hand-made ASTs from other tools.
Related errors
- illegal conversion: Cannot convert {:?} to LogicalExprOp
- illegal conversion: Cannot convert {:?} to Prop
- illegal conversion: Cannot convert {:?} to Prop::KeyValue
- illegal conversion: Cannot convert {:?} to Expr::Lit
- illegal conversion: Cannot convert {:?} to key, value
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/81b35b1737985e2e.
Report an issue: GitHub.