facebook/relay · error · Error

NormalizationEngine: Expected @stream field `${field.name}`

Error message

NormalizationEngine: Expected @stream field `${field.name}` to have a __typename.

What it means

To normalize a streamed item Relay must know its concrete type. It uses the field's concreteType (from a inline fragment/typed field in the query) when available, otherwise it falls back to the `__typename` in the streamed item's data. If neither yields a string, Relay cannot create a normalization selector and throws. This usually means the server omitted `__typename` from the streamed item or the query selected the field without a type.

Source

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

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

    const typeName = field.concreteType ?? (data as $FlowFixMe).__typename;
    if (typeof typeName !== 'string') {
      throw err(
        'NormalizationEngine: Expected @stream field `' +
          field.name +
          '` to have a __typename.',
      );
    }

    const getDataID = this._options.getDataID;
    const itemID =
      (typeof getDataID === 'function'
        ? getDataID(data as $FlowFixMe, typeName)
        : null) ??
      prevIDs?.[itemIndex] ??
      generateClientID(parentID, storageKey, itemIndex);
    if (typeof itemID !== 'string') {
      throw err(
        'NormalizationEngine: Expected id of elements of field `' +
          storageKey +
          '` to be strings.',

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Add `__typename` to the selection set of the @stream field in your query
  2. Fix the server/gateway to include __typename in streamed incremental payloads
  3. Remove any middleware that deletes __typename from response data
  4. If the type is statically known, ensure the compiled query provides concreteType for the field

Example fix

// before
fragment F on Node { comments @stream { id text } }
// after
fragment F on Node { comments @stream { __typename id text } }
Defensive patterns

Strategy: validation

Validate before calling

if (field.concreteType == null && typeof payload.data?.__typename !== 'string') {
  throw new Error(`@stream field ${field.name} missing __typename`);
}

Type guard

function hasTypename(d: unknown): d is { __typename: string } {
  return typeof d === 'object' && d !== null && typeof (d as any).__typename === 'string';
}

Prevention

When it happens

Trigger: Normalizing a @stream item where field.concreteType is undefined AND the item data lacks a string `__typename` — e.g. the server doesn't echo __typename for streamed list items, or the query selects the streamed field without __typename and without a concrete type.

Common situations: Custom/older GraphQL servers that don't add __typename to incremental payloads, hand-rolled mock servers in tests forgetting __typename, or stripping __typename in a network-layer middleware (e.g. after apollo-link-style normalization).

Related errors


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