Repository navigation
Open
Labels
Status: UnconfirmedA potential issue that we haven't yet confirmed as a bugA potential issue that we haven't yet confirmed as a bug
Description
When passing nested FormData instances as arguments to a Server Action (encodeReply / decodeReply), any FormData object corresponding to part ID 10 or greater (i.e. refId >= 10) has all its entries silently discarded and is decoded as an empty FormData (data.entries().next().done === true).
This is caused by an integer-radix mismatch between the serializer and deserializer:
- In
packages/react-client/src/ReactFlightReplyClient.js:707, field entries are keyed with a decimal prefix:Whenconst prefix = formFieldPrefix + '_' + refId + '_';
refIdis10, the entries are appended asformFieldPrefix + '_10_' + key. - The reference token emitted by
serializeFormDataReference(packages/react-client/src/ReactFlightReplyClient.js:138) is formatted as hexadecimal:Whenfunction serializeFormDataReference(id: number): string { return '$K' + id.toString(16); }
refIdis10, it emits'$Ka'. - In
packages/react-server/src/ReactFlightReplyServer.js:1765-1773, the server slices off'$K'and reconstructs the prefix directly from the hex string without callingparseInt(..., 16):The server looks forcase 'K': { const stringId = value.slice(2); const formPrefix = response._prefix + '_' + stringId + '_'; return consumeBackingEntries(response._formData, formPrefix); }
response._prefix + '_a_'. - In
consumeBackingEntries(packages/react-server/src/ReactFlightReplyBackingFormData.js:130-141), the key_10_...failskey.startsWith(formPrefix)(_a_). The loop immediately breaks, returning an emptyFormDataand leavingkeyPointerat0, causing subsequentFormDataobjects in the payload to also fail and return empty.
Every other handler inReactFlightReplyServer.js(case 'B',case 'Q',case 'W',case 'A',case 'R', etc.) parses the hex reference ID to integer viaparseInt(..., 16)before constructing the key.case 'K'is the only handler that omitted this step.
React version: 19.3.0 (main/ commit16a1d5d)
React version:
Steps To Reproduce
- Call a Server Action with a payload containing 10 or more
FormDataobjects (or a payload with 9 other objects like Maps/Sets/Blobs followed by aFormData). - Inside the server action handler, inspect
Array.from(formData.entries())or callformData.get('someKey')on the 10thFormData. - Observe that items 0 to 8 retain their entries, but item 9 (
refId = 10) is completely empty and returnsnullfor all keys.
Link to code example:
A minimal reproduction test case added topackages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js:
it('can pass the 10th or later FormData in a reply payload', async () => {
const list = [];
for (let i = 0; i < 12; i++) {
const fd = new FormData();
fd.set('key_' + i, 'value_' + i);
list.push(fd);
}
const body = await ReactServerDOMClient.encodeReply(list);
const decodedList = await ReactServerDOMServer.decodeReply(
body,
webpackServerMap,
);
expect(decodedList.length).toBe(12);
// Fails on index 9 (refId 10):
for (let i = 0; i < 12; i++) {
expect(decodedList[i].get('key_' + i)).toBe('value_' + i);
}
});Run with:
node ./scripts/jest/jest-cli.js --testPathPatterns="ReactFlightDOMReply-test"Link to code example:
The current behavior
decodedList[9].get('key_9')returnsnull.Array.from(decodedList[9].entries())returns[].decodedList[10]anddecodedList[11]also return empty due to the cursor remaining frozen.- Data is silently dropped with no errors or warnings thrown.
The expected behavior
decodedList[9].get('key_9')returns"value_9".- All
FormDataobjects at index 9 and above should retain all keys and values across the serialization boundary.
Metadata
Metadata
Assignees
Labels
Status: UnconfirmedA potential issue that we haven't yet confirmed as a bugA potential issue that we haven't yet confirmed as a bug
Activity
Triage react/react#38038: classify bug, mirror as issue-triage#243
lazerg commentedon Oct 30, 2026
This is already fixed in #37879, which has the same
parseInt(stringId, 16)change plus a test.