facebook/relay · error · Error

NormalizationEngine: Expected record `${parentID}` to have f

Error message

NormalizationEngine: Expected record `${parentID}` to have fetched field `${field.name}` with @stream.

What it means

When normalizing an incremental @stream response, Relay looks up the linked-record IDs that the parent record got during normalization of the original field. If that field on the parent record has no linked IDs (getLinkedRecordIDs returned null), Relay cannot know where in the list this streamed item belongs and throws. It indicates the @stream item arrived for a field the parent record doesn't have as a plural linked field — usually the base response was never normalized, a normalization option (e.g. getDataID or path handling) diverged, or the server sent a stream payload for the wrong field/path.

Source

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

    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.',
      );
    }

    const finalPathEntry = path[path.length - 1];
    const itemIndex = parseInt(finalPathEntry, 10);
    if (itemIndex !== finalPathEntry || itemIndex < 0) {
      throw err(
        'NormalizationEngine: Expected path for @stream to end in a ' +
          'positive integer index, got `' +
          String(finalPathEntry) +
          '`',
      );
    }

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure the base response containing the streamed field is normalized into the same record source before normalizing @stream incremental payloads
  2. Verify the variables passed to normalize for the incremental payload match those of the base response so storageKey is identical
  3. Check your network layer forwards incremental payloads in order and includes matching label/path metadata
  4. Clear/rebuild the store if a stale record source from an older normalization is being reused

Example fix

// before: normalizing stream payloads without the base response
incrementalResponses.forEach(r => engine.normalize(r));
// after: normalize base first, then incrementals
engine.normalize(baseResponse);
incrementalResponses.forEach(r => engine.normalize(r));
Defensive patterns

Strategy: validation

Validate before calling

const parentRecord = store.getSource().get(parentID);
const storageKey = getStorageKey(field, variables);
if (parentRecord?.getLinkedRecordIDs(storageKey) == null) {
  throw new Error(`Base response for @stream field ${storageKey} not normalized yet`);
}

Prevention

When it happens

Trigger: Calling normalize() on a @stream incremental payload whose parent record lacks linked IDs for storageKey — e.g. the base response was not normalized first, getStorageKey(field, variables) yields a key different from the one used in the base normalization (different variable values), or a custom environment re-ordered/streamed payloads out of order so the field was never registered.

Common situations: Custom network layers delivering incremental @stream chunks without the root response, replaying incremental payloads against a fresh/incomplete record source, or mismatched variables between base and incremental normalizations.

Related errors


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