What kind of issue is this?
Link to repro
https://github.com/ambar/react-compiler-symbol-loc-repro
Repro steps
babel-plugin-react-compiler uses an internal sentinel const GeneratedSource = Symbol() for "no source location" and writes it into the loc field of synthesized AST nodes. Babel defines Node.loc: SourceLocation | null — a Symbol value violates that contract. The sentinel leaks from the compiler output to any downstream AST consumer.
Minimal standalone repro (no React, no Metro, no bundler — just Babel + Node IPC):
git clone <repro url> # or paste the 5 files inline (see below)
cd react-compiler-symbol-loc-repro
npm install
node repro.js
Expected output (abridged):
─── Part 1: AST contract violation ─────────────────────────
Found 1 AST node(s) with `loc: Symbol()` in the babel-plugin-react-compiler output.
path=ast.program.body[5].body.body[15].consequent.body[1].expression.right
type=Identifier name=_t loc=Symbol()
─── Part 2: V8 structured-clone fails on this AST ──────────
v8.serialize(ast) threw: Symbol() could not be cloned.
─── Part 3: child_process.send reproduces the original error ─
parent process.send threw synchronously: Symbol() could not be cloned.
The synthesized _t Identifier appears on the RHS of an AssignmentExpression (label = _t) that React Compiler generates when it rewrites the memoization scope for:
export function Example({state, getLabels, colors, onTap}) {
const session = useMemo(() => ({state}), [state]);
if (session.state === 'off') return null;
const handleTap = () => onTap?.(session.state);
const {label, tint, glyph} = visualFor(session.state, getLabels);
return (
<button aria-label={label} onClick={handleTap} style={{background: tint}}>
<span>
{session.state === 'listening' ? <em>…</em> : glyph(colors.fg)}
<span style={{color: colors.fg}}>{label}</span>
</span>
</button>
);
}
Required conditions (I narrowed it down):
- At least one hook call at the top so the component is actually compiled (otherwise the compiler bails out).
- An early
return null after the hooks.
const {label, ...} = someFn(...) destructure from a function call.
- The destructured names referenced from 2+ independent JSX memo scopes, forcing the compiler to hoist them into a shared reactive cache slot instead of keeping them scope-local.
The compiler then produces (abridged):
if ($[N] !== ...) {
var _visualFor = visualFor(...),
_t = _visualFor.label, // _t is synthesized; no source location
tint = _visualFor.tint,
glyph = _visualFor.glyph;
label = _t; // <-- _t Identifier has loc: Symbol()
...
}
How often does this bug happen?
Every time
What version of React are you using?
19.1.0
What version of React Compiler are you using?
babel-plugin-react-compiler
What kind of issue is this?
Link to repro
https://github.com/ambar/react-compiler-symbol-loc-repro
Repro steps
babel-plugin-react-compileruses an internal sentinelconst GeneratedSource = Symbol()for "no source location" and writes it into thelocfield of synthesized AST nodes. Babel definesNode.loc: SourceLocation | null— aSymbolvalue violates that contract. The sentinel leaks from the compiler output to any downstream AST consumer.Minimal standalone repro (no React, no Metro, no bundler — just Babel + Node IPC):
Expected output (abridged):
The synthesized
_tIdentifier appears on the RHS of anAssignmentExpression(label = _t) that React Compiler generates when it rewrites the memoization scope for:Required conditions (I narrowed it down):
return nullafter the hooks.const {label, ...} = someFn(...)destructure from a function call.The compiler then produces (abridged):
How often does this bug happen?
Every time
What version of React are you using?
19.1.0
What version of React Compiler are you using?
babel-plugin-react-compiler