facebook/relay · error
Encountered an unexpected ReaderSelection variant in RelayRe
Error message
Encountered an unexpected ReaderSelection variant in RelayRecordSourceProxy. This indicates a bug in Relay.
What it means
createUpdatableProxy walks reader selections to build updatable proxies for optimistic updates. ReaderSelection kinds like RequiredField, CatchField, Stream, RelayResolver, and RelayLiveResolver are not supported inside updatable mutation proxies; hitting one throws this 'bug in Relay' invariant via the explicit case branch.
Source
Thrown at packages/relay-runtime/mutations/createUpdatableProxy.js:190
break;
case 'FragmentSpread':
// Explicitly ignore
break;
case 'Condition':
case 'ActorChange':
case 'InlineDataFragmentSpread':
case 'AliasedInlineFragmentSpread':
case 'ClientEdgeToClientObject':
case 'ClientEdgeToServerObject':
case 'Defer':
case 'ModuleImport':
case 'RequiredField':
case 'CatchField':
case 'Stream':
case 'RelayResolver':
case 'RelayLiveResolver':
// These types of reader nodes are not currently handled.
throw new Error(
'Encountered an unexpected ReaderSelection variant in RelayRecordSourceProxy. This indicates a bug in Relay.',
);
default:
selection.kind as empty;
throw new Error(
'Encountered an unexpected ReaderSelection variant in RelayRecordSourceProxy. This indicates a bug in Relay.',
);
}
}
}
function createSetterForPluralLinkedField(
selection: ReaderLinkedField,
variables: Variables,
updatableProxyRootRecord: RecordProxy,
recordSourceProxy: RecordSourceProxy,
) {
return function set(newValue: ReadonlyArray<{__id: string, ...}>) {View on GitHub (pinned to 668b1b85e0)
Solutions
- Remove or restructure @required/@catch/@stream/Resolver fields from the selection set used by the updatable mutation
- Split the mutation so unhandled reader kinds are not part of the updatable region (select plain scalar/object fields only)
- Upgrade Relay to a newer version that may support these reader variants in updatable proxies
- If nothing above applies, reproduce and file a Relay bug — this path is an internal invariant
Example fix
// before
fragment F on User { name @catch { message } }
// after (updatable fragment)
fragment F on User { name } Defensive patterns
Strategy: try-catch
Validate before calling
// Keep updatable fragments limited to plain fields; detect unsupported directives before compiling mutations // e.g. lint rule: fail if @catch/@required/@stream/Resolver fields appear in updatable fragments
Try / catch
try {
commitMutation(env, {mutation, variables, optimisticUpdater: store => {...}});
} catch (e) {
if (e.message.includes('unexpected ReaderSelection variant')) {
// retry without the optimistic updater / report the artifact
}
} Prevention
- Do not use @catch/@required/@stream or Resolver fields inside updatable mutation fragments
- Pin and align relay versions so reader selection kinds are supported
- Keep optimistic updaters minimal — prefer optimisticResponse
- Add an ErrorBoundary around mutation-triggering UI
When it happens
Trigger: Performing an optimistic/updatable mutation whose @required/@catch-wrapped fields, @stream directives, or Relay Resolver / Live Resolver fields fall inside the mutation's updatable selection set.
Common situations: Adding @catch or @required directives or a client-side Resolver field to a type that participates in an updatable mutation; migrating a schema to Relay Resolvers while keeping legacy optimistic updates over those fields.
Related errors
- useRefetchableFragmentNode: Unexpected action type
- LiveResolverCache: `ServerWeak` normalization info must not
- Expected a named import for Relay Resolvers
- Unexpected ResolverModuleReference
- Unexpected DynamicImport
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/3e290ca7ff5b4fa9.
Report an issue: GitHub.