facebook/relay · error · Error

NormalizationEngine: Expected @stream to be used on a plural

Error message

NormalizationEngine: Expected @stream to be used on a plural field.

What it means

When normalizing an @stream payload, Relay extracts the first selection from the streamed field node and requires it to be a LinkedField with `plural: true`, because @stream only makes sense on list-typed fields where items arrive incrementally. If the field node is missing, not a LinkedField, or is singular, Relay throws since it cannot stream a non-plural field.

Source

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

  // ---------------------------------------------------------------------------
  // Private: @stream handling
  // ---------------------------------------------------------------------------

  _processStream(
    response: GraphQLResponseWithData,
    path: ReadonlyArray<unknown>,
    placeholder: StreamPlaceholder,
  ): NormalizationResult {
    const {node, parentID, variables} = placeholder;

    // Find the LinkedField where @stream was applied
    const field = node.selections[0];
    if (
      field == null ||
      field.kind !== 'LinkedField' ||
      field.plural !== true
    ) {
      throw err(
        'NormalizationEngine: Expected @stream to be used on a plural field.',
      );
    }

    const {
      fieldPayloads,
      itemID,
      itemIndex,
      prevIDs,
      relayPayload,
      storageKey,
    } = this._normalizeStreamItem(
      response,
      parentID,
      field,
      variables,
      path,
      placeholder.path,

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Find the field with @stream in the query and verify its GraphQL type is a list ([Type]); remove @stream if it is singular.
  2. Re-fetch/refresh the query so client-side normalized AST matches the current schema if the field's type changed.
  3. Ensure the server only streams fields the client annotated with @stream and that both sides agree the field is plural.
  4. Update the persisted query / compiled artifacts if the query was compiled before a schema change.

Example fix

// before
query { friends @stream(initialCount: 1) { name } } // if friends is a single object
// after
query { friend @stream(initialCount: 1) { name } } // only if friend: [Friend] is plural; otherwise drop @stream
Defensive patterns

Strategy: validation

Validate before calling

// Validate at build/query time: only apply @stream to list fields
function assertStreamOnPlural(field) {
  if (field.directives.some(d => d.name === 'stream') && !field.isList) {
    throw new Error('@stream requires a plural (list) field: ' + field.name);
  }
}

Type guard

function isPluralLinkedField(node) {
  return node != null && node.kind === 'LinkedField' && node.plural === true;
}

Try / catch

try {
  normalizationEngine.processIncrementalResponse(response);
} catch (e) {
  if (String(e.message).includes('@stream to be used on a plural field')) {
    console.error('Query misuses @stream on a non-list field — fix the query');
  } else throw e;
}

Prevention

When it happens

Trigger: A @stream directive is placed on a singular (non-list) field in the query, or the normalized AST node for the streamed field is not a plural LinkedField — e.g. the selection node passed to `_processStream` (via processIncrementalResponse/_registerPlaceholders) corresponds to a scalar or single object field.

Common situations: Developers adding @stream to a field whose GraphQL type is not a list; schema changes turning a formerly plural field singular while cached queries still use @stream; client/server directive mismatch where the server streams a field the client typed as singular.

Related errors


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