facebook/react · error · Error

571

571

Error message

Maximum array nesting exceeded. Large nested arrays can be dangerous. Try adding intermediate objects.

What it means

When decoding a Flight reply (a server action invocation submitted from the client), React accumulates the total number of slots in nested arrays via bumpArrayCount. Once the count exceeds the response array size limit (default 1,000,000, configurable through createResponse's arraySizeLimit) inside a forked array (an array with siblings), it throws. This is a DoS guard against pathologically large nested arrays.

Source

Thrown at packages/react-server/src/ReactFlightReplyServer.js:701

  return value;
}

type NestedArrayContext = {
  // Keeps track of how many slots, bytes or characters are in nested arrays/strings/typed arrays.
  count: number,
  // A single child is itself not harmful. There needs to be at least one parent array with more
  // than one child.
  fork: boolean,
};

function bumpArrayCount(
  arrayContext: NestedArrayContext,
  slots: number,
  response: Response,
): void {
  const newCount = (arrayContext.count += slots);
  if (newCount > response._arraySizeLimit && arrayContext.fork) {
    throw new Error(
      'Maximum array nesting exceeded. Large nested arrays can be dangerous. Try adding intermediate objects.',
    );
  }
}

type InitializationReference = {
  handler: InitializationHandler,
  parentObject: Object,
  key: string,
  map: (
    response: Response,
    model: any,
    parentObject: Object,
    key: string,
  ) => any,
  path: Array<string>,
  arrayRoot: null | NestedArrayContext,
};

View on GitHub (pinned to eafeac097b)

Solutions

  1. Restructure the argument: wrap nesting levels in plain objects ({children: [...]}) or send a flattened list of ids/patches - the guard counts array slots, not object keys.
  2. Send only deltas or ids and rehydrate the full structure on the server.
  3. Split the work across multiple server action invocations.
  4. If you control the runtime (custom RSC framework), pass a higher arraySizeLimit to createResponse after assessing your DoS exposure.

Example fix

// before — client sends a deeply nested tree to a server action
await saveTree([[rows.map(r => [r.cells])]]); // millions of total array slots

// after — flatten to records
await saveTree(rows.map(r => ({id: r.id, values: r.cells.flat()})));
Defensive patterns

Strategy: validation

Validate before calling

const ARRAY_SLOT_LIMIT = 1_000_000;
function estimateArraySlots(v: unknown): number {
  if (Array.isArray(v)) {
    let n = v.length;
    for (const c of v) n += estimateArraySlots(c);
    return n;
  }
  if (typeof v === 'string') return v.length;
  if (typeof v === 'bigint') return String(v).length;
  if (ArrayBuffer.isView(v)) return v.byteLength;
  if (v && typeof v === 'object') {
    let n = 0;
    for (const k in v) n += estimateArraySlots((v as any)[k]);
    return n;
  }
  return 0;
}
// run before invoking the action
if (estimateArraySlots(args) > ARRAY_SLOT_LIMIT) {
  throw new Error('Action payload too large; send ids/deltas instead');
}

Prevention

When it happens

Trigger: A server action argument contains arrays whose cumulative element count exceeds the limit - deep trees, big matrices, full state snapshots. Large strings, BigInts, and typed arrays referenced inside the arrays also add their length/byteLength to the count. Only fires when the nested array has at least one parent array with more than one child (fork = true).

Common situations: Passing an entire client state tree to a server action; batch-edit forms that serialize every row; upgrading React to a version where the guard landed, so payloads that previously worked now throw; custom frameworks calling decodeReply on untrusted FormData with the default limit.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/af44ea37382a9c48. Report an issue: GitHub.