swc-project/swc · error

empty expression container

Error message

empty expression container

What it means

During JSX-to-function conversion, process_attr_value (jsx/mod.rs:539) must turn every JSX attribute value into an expression. A JSXExprContainer whose expr is JSXEmptyExpr — an attribute written `attr={}` — has no value to emit, so the transform panics with "empty expression container". The SWC parser normally rejects empty expression containers, so in practice this fires on programmatically built, plugin-modified, or error-recovered ASTs rather than plain source text.

Source

Thrown at crates/swc_ecma_transforms_react/src/jsx/mod.rs:545

        .as_arg()
    }

    fn jsx_dev_self_arg(&self) -> ExprOrSpread {
        if self.self_ctx.can_add_self() {
            ThisExpr { span: DUMMY_SP }.as_arg()
        } else {
            Expr::undefined(DUMMY_SP).as_arg()
        }
    }

    /// Process JSX attribute value, handling JSXElements and JSXFragments
    fn process_attr_value(&mut self, value: Option<JSXAttrValue>) -> Box<Expr> {
        match value {
            Some(JSXAttrValue::JSXElement(el)) => Box::new(self.jsx_elem_to_expr(*el)),
            Some(JSXAttrValue::JSXFragment(frag)) => Box::new(self.jsx_frag_to_expr(frag)),
            Some(JSXAttrValue::JSXExprContainer(container)) => match container.expr {
                JSXExpr::Expr(e) => e,
                JSXExpr::JSXEmptyExpr(_) => panic!("empty expression container"),
                #[cfg(swc_ast_unknown)]
                _ => panic!("unable to access unknown nodes"),
            },
            Some(v) => jsx_attr_value_to_expr(v).expect("empty expression container?"),
            None => true.into(),
        }
    }

    fn inject_runtime<T, F>(&mut self, body: &mut Vec<T>, inject: F)
    where
        T: StmtLike,
        // Fn(Vec<(local, imported)>, src, body)
        F: Fn(Vec<(Ident, IdentName)>, &str, &mut Vec<T>),
    {
        if self.runtime == Runtime::Automatic {
            if let Some(local) = self.import_create_element.take() {
                inject(
                    vec![(local, quote_ident!("createElement"))],

View on GitHub (pinned to 5176682b65)

Solutions

  1. Find the `{}` attribute in the offending element and give it a real expression, or remove the attribute entirely.
  2. If a plugin produced it, fix the plugin to drop the whole JSXAttr when clearing its value.
  3. Run a pre-flight visitor that reports JSXExprContainer-with-JSXEmptyExpr before invoking the react transform.

Example fix

// before (AST shape reaching the transform)
<Foo bar={} />

// after
<Foo bar={"" />}
// or simply: <Foo />
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight scan: reject JSX attributes with empty expression containers.
#[derive(Default)]
struct EmptyJsxAttr { bad: Vec<Span> }
impl Visit for EmptyJsxAttr {
    fn visit_jsx_attr(&mut self, attr: &JSXAttr) {
        if let Some(JSXAttrValue::JSXExprContainer(c)) = &attr.value {
            if matches!(c.expr, JSXExpr::JSXEmptyExpr(_)) {
                self.bad.push(attr.span);
            }
        }
        attr.visit_children_with(self);
    }
}

Prevention

When it happens

Trigger: Feeding the react JSX pass an AST in which some JSXAttr.value is JSXExprContainer { expr: JSXEmptyExpr } — i.e. `<Foo bar={} />` produced by a lenient parser, an AST builder, or a swc plugin/codemod that emptied the container but kept the attribute.

Common situations: Codemods or plugins that delete an attribute's expression while leaving the container; hand-written AST fixtures; parsing with error recovery enabled and ignoring diagnostics.

Related errors


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