facebook/relay · error · Error

NormalizationEngine: Expected the GraphQL @stream payload `d

Error message

NormalizationEngine: Expected the GraphQL @stream payload `data` value to be an object.

What it means

In _normalizeStreamItem, Relay destructures `data` from the incremental @stream response and requires it to be an object (the streamed item's fields). If `data` is a primitive, array-unexpectedly-null, or undefined (anything with typeof !== 'object'), Relay cannot normalize the item and throws.

Source

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

  _normalizeStreamItem(
    response: GraphQLResponseWithData,
    parentID: string,
    field: NormalizationLinkedField,
    variables: Variables,
    path: ReadonlyArray<unknown>,
    normalizationPath: ReadonlyArray<string>,
  ): {
    fieldPayloads: Array<HandleFieldPayload>,
    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(

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Inspect the raw streamed payload and confirm `data` is an object; if the server sends null with errors, handle/inspect the `errors` field instead of normalizing.
  2. Fix the server so each @stream part carries an object `data` (or omit the part when there is no data).
  3. Check the network layer's multipart parsing — ensure each chunk is parsed as complete JSON before being passed to the Relay environment.
  4. Check whether a proxy or fetch wrapper transforms the response body and strips/replaces `data`.

Example fix

// before
handleNextChunk(rawPart); // data may be null
// after
if (rawPart.data != null && typeof rawPart.data === 'object') {
  handleNextChunk(rawPart);
} else {
  handleError(rawPart.errors);
}
Defensive patterns

Strategy: type-guard

Validate before calling

function hasObjectData(part) {
  return part != null && typeof part.data === 'object' && part.data !== null;
}
if (!hasObjectData(streamPart)) { /* route to error handling instead of normalizing */ }

Type guard

function isObjectPayload(res) {
  return typeof res === 'object' && res !== null &&
    'data' in res && typeof res.data === 'object' && res.data !== null;
}

Try / catch

try {
  normalizationEngine.processIncrementalResponse(response);
} catch (e) {
  if (String(e.message).includes('@stream payload `data`')) {
    handleStreamError(response.errors);
  } else throw e;
}

Prevention

When it happens

Trigger: A @stream incremental payload arrives whose `data` value is not an object — e.g. the server sends `data: null` for an errored stream item, a scalar, or the network layer mangles the multipart part so data is missing.

Common situations: Servers sending null data alongside errors on streamed items; custom network layers parsing multipart chunks incorrectly (JSON fragments concatenated or truncated); proxies/transcoders altering incremental payloads.

Related errors


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