diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
index 486773d5eb9..b85746965d2 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
@@ -2420,7 +2420,12 @@ function codegenPlace(cx: Context, place: Place): t.Expression | t.JSXText {
loc: place.loc,
});
const identifier = convertIdentifier(place.identifier);
- identifier.loc = place.loc as any;
+ /*
+ * Guard against GeneratedSource (a Symbol) leaking into Babel AST nodes.
+ * Babel requires Node.loc to be SourceLocation | null, so synthesized nodes
+ * without real source positions must use null, not the internal sentinel.
+ */
+ identifier.loc = place.loc !== GeneratedSource ? (place.loc as t.SourceLocation) : null;
return identifier;
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.expect.md
new file mode 100644
index 00000000000..d2a3d3a946c
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.expect.md
@@ -0,0 +1,180 @@
+
+## Input
+
+```javascript
+// @validatePreserveExistingMemoizationGuarantees
+import {useMemo} from 'react';
+
+function visualFor(state, getLabels) {
+ return {label: getLabels(state), tint: 'red', glyph: () => null};
+}
+
+// Repro: synthesized temporaries for destructured bindings from cross-scope
+// hoisting would have their Babel AST identifier node's .loc set to the
+// internal GeneratedSource Symbol sentinel instead of null, causing
+// v8.serialize / jest-worker IPC failures.
+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 (
+
+ );
+}
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
+import { useMemo } from "react";
+
+function visualFor(state, getLabels) {
+ const $ = _c(5);
+ let t0;
+ if ($[0] !== getLabels || $[1] !== state) {
+ t0 = getLabels(state);
+ $[0] = getLabels;
+ $[1] = state;
+ $[2] = t0;
+ } else {
+ t0 = $[2];
+ }
+ let t1;
+ if ($[3] !== t0) {
+ t1 = { label: t0, tint: "red", glyph: _temp };
+ $[3] = t0;
+ $[4] = t1;
+ } else {
+ t1 = $[4];
+ }
+ return t1;
+}
+
+// Repro: synthesized temporaries for destructured bindings from cross-scope
+// hoisting would have their Babel AST identifier node's .loc set to the
+// internal GeneratedSource Symbol sentinel instead of null, causing
+// v8.serialize / jest-worker IPC failures.
+function _temp() {
+ return null;
+}
+export function Example(t0) {
+ const $ = _c(27);
+ const { state, getLabels, colors, onTap } = t0;
+ let t1;
+ if ($[0] !== state) {
+ t1 = { state };
+ $[0] = state;
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const session = t1;
+ if (session.state === "off") {
+ return null;
+ }
+ let t2;
+ if ($[2] !== onTap || $[3] !== session.state) {
+ t2 = () => onTap?.(session.state);
+ $[2] = onTap;
+ $[3] = session.state;
+ $[4] = t2;
+ } else {
+ t2 = $[4];
+ }
+ const handleTap = t2;
+ let label;
+ let t3;
+ let t4;
+ let t5;
+ let t6;
+ if (
+ $[5] !== colors.fg ||
+ $[6] !== getLabels ||
+ $[7] !== handleTap ||
+ $[8] !== session.state
+ ) {
+ const { label: t7, tint, glyph } = visualFor(session.state, getLabels);
+ label = t7;
+ t4 = label;
+ t5 = handleTap;
+ t6 = { background: tint };
+ t3 = session.state === "listening" ? ... : glyph(colors.fg);
+ $[5] = colors.fg;
+ $[6] = getLabels;
+ $[7] = handleTap;
+ $[8] = session.state;
+ $[9] = label;
+ $[10] = t3;
+ $[11] = t4;
+ $[12] = t5;
+ $[13] = t6;
+ } else {
+ label = $[9];
+ t3 = $[10];
+ t4 = $[11];
+ t5 = $[12];
+ t6 = $[13];
+ }
+ let t7;
+ if ($[14] !== colors.fg) {
+ t7 = { color: colors.fg };
+ $[14] = colors.fg;
+ $[15] = t7;
+ } else {
+ t7 = $[15];
+ }
+ let t8;
+ if ($[16] !== label || $[17] !== t7) {
+ t8 = {label};
+ $[16] = label;
+ $[17] = t7;
+ $[18] = t8;
+ } else {
+ t8 = $[18];
+ }
+ let t9;
+ if ($[19] !== t3 || $[20] !== t8) {
+ t9 = (
+
+ {t3}
+ {t8}
+
+ );
+ $[19] = t3;
+ $[20] = t8;
+ $[21] = t9;
+ } else {
+ t9 = $[21];
+ }
+ let t10;
+ if ($[22] !== t4 || $[23] !== t5 || $[24] !== t6 || $[25] !== t9) {
+ t10 = (
+
+ );
+ $[22] = t4;
+ $[23] = t5;
+ $[24] = t6;
+ $[25] = t9;
+ $[26] = t10;
+ } else {
+ t10 = $[26];
+ }
+ return t10;
+}
+
+```
+
+### Eval output
+(kind: exception) Fixture not implemented
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.js
new file mode 100644
index 00000000000..4b979d8bb76
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-generated-source-symbol-in-babel-loc.js
@@ -0,0 +1,27 @@
+// @validatePreserveExistingMemoizationGuarantees
+import {useMemo} from 'react';
+
+function visualFor(state, getLabels) {
+ return {label: getLabels(state), tint: 'red', glyph: () => null};
+}
+
+// Repro: synthesized temporaries for destructured bindings from cross-scope
+// hoisting would have their Babel AST identifier node's .loc set to the
+// internal GeneratedSource Symbol sentinel instead of null, causing
+// v8.serialize / jest-worker IPC failures.
+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 (
+
+ );
+}