swc-project/swc · error

failed to parse jsx option {}: '{}' is not an expression

Error message

failed to parse jsx option {}: '{}' is not an expression

What it means

parse_expr_for_jsx (crates/swc_ecma_transforms_react/src/jsx/mod.rs:143) parses the strings used as JSX pragmas into an expression AST via parse_file_as_expr before the JSX transform runs. If the string is not a single valid JavaScript expression (a statement, empty string, unbalanced bracket, stray comma or call parens), parsing fails and the function panics with the option name and the raw string. It is used for the `pragma` and `pragmaFrag` config options (jsc.transform.react) and for `/** @jsx */` / `/** @jsxFrag */` docblock comments.

Source

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

        None,
        &mut Vec::new(),
    )
    .map_err(|e| {
        if HANDLER.is_set() {
            HANDLER.with(|h| {
                e.into_diagnostic(h)
                    .note("Failed to parse jsx pragma")
                    .emit()
            })
        }
    })
    .map(drop_span)
    .map(|mut expr| {
        apply_mark(&mut expr, top_level_mark);
        expr
    })
    .unwrap_or_else(|()| {
        panic!(
            "failed to parse jsx option {}: '{}' is not an expression",
            name, fm.src,
        )
    })
}

fn apply_mark(e: &mut Expr, mark: Mark) {
    match e {
        Expr::Ident(i) => {
            i.ctxt = i.ctxt.apply_mark(mark);
        }
        Expr::Member(MemberExpr { obj, .. }) => {
            apply_mark(obj, mark);
        }
        _ => {}
    }
}

View on GitHub (pinned to 5176682b65)

Solutions

  1. Use a plain identifier or member expression: "React.createElement", "h", "Vue.h" (and "React.Fragment" for pragmaFrag).
  2. Remove call parens/arguments — the pragma names a function, it is never invoked.
  3. If the value comes from a `/** @jsx */` comment pragma, fix or delete that comment line.

Example fix

// before (.swcrc)
{
  "jsc": { "transform": { "react": { "pragma": "React.createElement(" } } }
}

// after
{
  "jsc": { "transform": { "react": { "pragma": "React.createElement" } } }
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate a pragma string BEFORE constructing the react transform.
fn assert_valid_pragma(cm: &SourceMap, name: &str, src: &str) -> Result<(), String> {
    let fm = cm.new_source_file(SourceFile::new(), src.into());
    let mut errors = Vec::new();
    let ok = swc_ecma_parser::parse_file_as_expr(
        &fm, Syntax::default(), Default::default(), None, &mut errors,
    ).is_ok();
    if ok && errors.is_empty() { Ok(()) } else {
        Err(format!("option {name}: {src:?} is not a valid expression"))
    }
}

Try / catch

// Pass construction panics before any file is processed; wrap config load:
match std::panic::catch_unwind(|| jsx(cm.clone(), comments, options, m1, m2)) {
    Ok(pass) => Ok(pass),
    Err(payload) => Err(anyhow!("invalid react pragma config: {payload:?}")),
}

Prevention

When it happens

Trigger: Setting jsc.transform.react.pragma or pragmaFrag to a non-expression string such as "React.createElement(", "", "a,", "import x from 'y'", or a comment pragma value (module-jsx-pragma / module-jsx-pragma-frag paths at jsx/mod.rs:379 and :394) that fails to parse.

Common situations: Typos or trailing punctuation in .swcrc / next.config.js compiler options; migrating a Babel config where the pragma looked like a call ("h()"); environment-variable interpolation producing an empty pragma string.

Understand the failure class

Related errors


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