facebook/relay · error · Error

NormalizationEngine: Expected the parent record `${parentID}

Error message

NormalizationEngine: Expected the parent record `${parentID}` for @stream data to exist.

What it means

Relay tracks parent records for each streamed field in `_parentRecords` when the initial items arrive. When a subsequent @stream item arrives, _normalizeStreamItem looks up the parent record by `path`-derived `parentID` to append/attach the new item. If no record was registered for that ID (parent response never normalized, path mismatch, or store cleared), Relay throws.

Source

Thrown at packages/relay-runtime/store/NormalizationEngine.js:488

    itemID: string,
    itemIndex: number,
    prevIDs: Array<?string>,
    relayPayload: RelayResponsePayload,
    storageKey: string,
  } {
    const {data} = response;
    if (typeof data !== 'object') {
      throw err(
        'NormalizationEngine: Expected the GraphQL @stream payload `data` ' +
          'value to be an object.',
      );
    }
    const responseKey = field.alias ?? field.name;
    const storageKey = getStorageKey(field, variables);

    const parentEntry = this._parentRecords.get(parentID);
    if (parentEntry == null) {
      throw err(
        'NormalizationEngine: Expected the parent record `' +
          parentID +
          '` for @stream data to exist.',
      );
    }
    const {fieldPayloads, record: parentRecord} = parentEntry;

    const prevIDs = RelayModernRecord.getLinkedRecordIDs(
      parentRecord,
      storageKey,
    );
    if (prevIDs == null) {
      throw err(
        'NormalizationEngine: Expected record `' +
          parentID +
          '` to have fetched field `' +
          field.name +
          '` with @stream.',

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure the initial response (containing the streamed field's base record) is fully normalized in the same Relay environment/store before incremental items are processed.
  2. Verify the server's incremental `path` values match the query aliases/structure so Relay derives the correct parentID.
  3. Check that no store reset/GC (e.g. environment.store.reset(), retention expiry) runs between the base response and the stream items.
  4. Fix custom network layers to preserve part ordering (base response first, then incrementals) and route all parts to one environment.
  5. In mocks/tests, always emit the complete initial response before any @stream incremental parts.

Example fix

// before
parts.forEach(part => processPart(part)); // stream items may arrive before base record
// after
const [base, ...incrementals] = parts;
processPart(base);
incrementals.forEach(processPart); // parent record now registered
Defensive patterns

Strategy: validation

Validate before calling

// Ensure base response was processed and parent record exists before stream items
const record = environment.getStore().getSource().get(parentIdFromPath(part.path));
if (record == null) {
  throw new Error('Parent record missing; process the base response first');
}

Type guard

function parentRecordExists(environment, path) {
  const id = path && path.length ? String(path[0]) : null;
  return id != null && environment.getStore().getSource().get(id) != null;
}

Try / catch

try {
  normalizationEngine.processIncrementalResponse(response);
} catch (e) {
  if (String(e.message).includes('for @stream data to exist')) {
    console.error('Parent record missing — base response dropped or store reset:', response.path);
  } else throw e;
}

Prevention

When it happens

Trigger: An incremental @stream item arrives whose `path` points to a parent record that was never registered — e.g. the initial (non-incremental) response part was dropped or processed by a different store/Relay environment, the path in the payload doesn't match the query structure, or records were garbage-collected between the initial response and stream items.

Common situations: Multipart chunks routed to different Relay environments or a store reset between the initial response and streamed items; custom network layers reordering parts so a stream item is processed before its parent; server emitting paths inconsistent with the query's aliasing; test mocks emitting stream items without the base response.

Related errors


AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02). Data as JSON: /api/errors/a293660c1f05d70d. Report an issue: GitHub.