facebook/relay · error · Error
NormalizationEngine: Expected the parent record `${parentID}
Error message
NormalizationEngine: Expected the parent record `${parentID}` for @stream data to exist. What it means
Relay tracks parent records for each streamed field in `_parentRecords` when the initial items arrive. When a subsequent @stream item arrives, _normalizeStreamItem looks up the parent record by `path`-derived `parentID` to append/attach the new item. If no record was registered for that ID (parent response never normalized, path mismatch, or store cleared), Relay throws.
Source
Thrown at packages/relay-runtime/store/NormalizationEngine.js:488
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(
parentRecord,
storageKey,
);
if (prevIDs == null) {
throw err(
'NormalizationEngine: Expected record `' +
parentID +
'` to have fetched field `' +
field.name +
'` with @stream.',View on GitHub (pinned to 668b1b85e0)
Solutions
- Ensure the initial response (containing the streamed field's base record) is fully normalized in the same Relay environment/store before incremental items are processed.
- Verify the server's incremental `path` values match the query aliases/structure so Relay derives the correct parentID.
- Check that no store reset/GC (e.g. environment.store.reset(), retention expiry) runs between the base response and the stream items.
- Fix custom network layers to preserve part ordering (base response first, then incrementals) and route all parts to one environment.
- In mocks/tests, always emit the complete initial response before any @stream incremental parts.
Example fix
// before parts.forEach(part => processPart(part)); // stream items may arrive before base record // after const [base, ...incrementals] = parts; processPart(base); incrementals.forEach(processPart); // parent record now registered
Defensive patterns
Strategy: validation
Validate before calling
// Ensure base response was processed and parent record exists before stream items
const record = environment.getStore().getSource().get(parentIdFromPath(part.path));
if (record == null) {
throw new Error('Parent record missing; process the base response first');
} Type guard
function parentRecordExists(environment, path) {
const id = path && path.length ? String(path[0]) : null;
return id != null && environment.getStore().getSource().get(id) != null;
} Try / catch
try {
normalizationEngine.processIncrementalResponse(response);
} catch (e) {
if (String(e.message).includes('for @stream data to exist')) {
console.error('Parent record missing — base response dropped or store reset:', response.path);
} else throw e;
} Prevention
- Process the initial response before any incremental parts, in the same Relay environment.
- Avoid store resets / GC between the base response and streamed items.
- Preserve multipart part ordering in custom network layers.
- Verify server incremental paths match query aliases and structure.
- In tests, always emit the full base response before @stream incrementals.
When it happens
Trigger: An incremental @stream item arrives whose `path` points to a parent record that was never registered — e.g. the initial (non-incremental) response part was dropped or processed by a different store/Relay environment, the path in the payload doesn't match the query structure, or records were garbage-collected between the initial response and stream items.
Common situations: Multipart chunks routed to different Relay environments or a store reset between the initial response and streamed items; custom network layers reordering parts so a stream item is processed before its parent; server emitting paths inconsistent with the query's aliasing; test mocks emitting stream items without the base response.
Related errors
- NormalizationEngine: Expected incremental response to have `
- NormalizationEngine: Expected @stream to be used on a plural
- NormalizationEngine: Expected the GraphQL @stream payload `d
- 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/a293660c1f05d70d.
Report an issue: GitHub.