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
- Use a plain identifier or member expression: "React.createElement", "h", "Vue.h" (and "React.Fragment" for pragmaFrag).
- Remove call parens/arguments — the pragma names a function, it is never invoked.
- 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
- Keep pragma values as plain identifiers/member expressions ("h", "React.createElement", "React.Fragment").
- Never include call parens, arguments, or statements in pragma/pragmaFrag.
- If pragmas come from env vars, validate they are non-empty at config load time.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- empty expression container
- Duplicate __source is found
- Duplicate __self is found
- attr_to_prop(JSXEmptyExpr)
- illegal conversion: Cannot convert {:?} to ExprOutput
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/eb1ed4dc247e17d2.
Report an issue: GitHub.