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
- Find the field with @stream in the query and verify its GraphQL type is a list ([Type]); remove @stream if it is singular.
- Re-fetch/refresh the query so client-side normalized AST matches the current schema if the field's type changed.
- Ensure the server only streams fields the client annotated with @stream and that both sides agree the field is plural.
- 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
- Only annotate list-typed GraphQL fields with @stream.
- Regenerate compiled queries after schema type changes.
- Keep client @stream usage and server streaming behavior aligned.
- Lint custom Relay AST transforms to verify plural flags on streamed fields.
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
- NormalizationEngine: Expected incremental response to have `
- NormalizationEngine: Expected the GraphQL @stream payload `d
- NormalizationEngine: Expected the parent record `${parentID}
- NormalizationEngine: Expected @stream field `${field.name}`
- BabelPluginRelay: Expected exactly one definition per graphq
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/fafc4073606d38c7.
Report an issue: GitHub.