Skip to content

React Flight serializer: symbol falls through the whole typeof chain, re-check at throw site is true — Next.js App Router renders all 500 #6546

Description

@proggeramlug

Summary

In Next.js 16's compiled React Flight serializer (next/dist/compiled/next-server/app-page-turbo.runtime.prod.js, the ~318KB outlined-model writer closure), a Symbol.for("react.fragment") value falls through the entire typeof dispatch chain to the tail throw — even though re-evaluating the exact same comparison at the throw site returns true.

Probe installed immediately before the tail throw Error("Type "+typeof a+"..."):

console.error("PRB-TYPE2 recheck="+("symbol"==typeof a)+" strict="+("symbol"===typeof a)+" t="+typeof a+" str="+String(a));

Output under perry (per request):

PRB-TYPE2 recheck=true strict=true t=symbol str=Symbol(react.fragment)

i.e. control passed the function's if("symbol"==typeof a){...} arm without taking it, then the probe — adjacent statements, same variable — sees the comparison as TRUE. The compiled arm evaluated FALSE for the same value the probe sees as a symbol.

Impact

This is the current blocker for Next.js 16 standalone serving (with #6522, #6527, #6537, #6545 and the fetch-super fix applied, the server boots and stays alive): every App Router render 500s with either
Error: Only global symbols received from Symbol.for(...) can be passed to Client Components. The symbol Symbol.for(undefined) cannot be found… or
Error: Type symbol is not supported in Client Component props. (which of the two Flight sites fires varies per build), plus paired Unhandled Rejection: undefined.

What was ruled out (all byte-identical to node in isolation)

  • Symbol.for/Symbol.keyFor/.description round-trips, incl. cross-module identity (/tmp-style repros).
  • "symbol"==typeof x dispatch chains on registered/local/well-known symbols in a small function.
  • The registry's Box::leak'd symbols pass js_value_typeof (SYMBOL_POINTERS registry covers non-arena symbols).

The failing function's IR shows the symbol check compiled generically (js_value_typeof + js_string_equals against the interned "symbol"), which is sound — so either the executed check reads a stale/desynced slot for the reassigned minified local a (the function reassigns a heavily; possibly the boxed-vs-direct dual-slot family, cf. #6328/#6345), or codegen emitted a duplicated/specialized variant of the typeof chain (typed-feedback fast path) whose symbol classification diverges, and that variant is the one executing.

Note js_value_typeof returns per-thread cached StringHeaders for its results while the compiled comparison goes through js_string_equals (content compare) — identity vs content shouldn't matter here, but the executed-variant question is open.

Repro

perry compile the gscmaster Next.js 16 standalone app (or any App Router app) at main + PR #6545, boot, curl /<any-page>. The probe text above can be inserted at the single "Type "+typeof a+" is not supported in Client Component props" site in app-page-turbo.runtime.prod.js.

Verified at main 7279998 + #6545 + the fetch-super fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions