facebook/relay · error
Expected client edge backing field to be transformed into ex
Error message
Expected client edge backing field to be transformed into exactly one primitive.
What it means
While building a client edge (Relay resolver-backed edge) in codegen, the compiler transforms the backing field selection into primitives and expects exactly one result. If the backing field expanded into zero or multiple primitives (e.g. a fragment with multiple selections or an empty selection), this panic fires, meaning the client-edge metadata is inconsistent with the actual document.
Source
Thrown at compiler/crates/relay-codegen/src/build_ast.rs:2044
})
}
})
}
// This function creates a node that is the UNION of the nodes that would be created for read time resolvers
// and for exec time resolvers (so runtime has ALL the information it needs to run for both resolver modes.)
// For C2C (client-to-client) edges, we emit ClientEdgeToClientObject with model resolvers.
// For C2S (client-to-server) edges, we emit ClientEdgeToServerObject with the operation reference.
fn build_client_edge_exec_and_read_time(
&mut self,
context: &mut ContextualMetadata,
client_edge_metadata: ClientEdgeMetadata<'_>,
) -> Primitive {
let backing_field_primitives =
self.build_selections_from_selection(context, client_edge_metadata.backing_field);
if backing_field_primitives.len() != 1 {
panic!(
"Expected client edge backing field to be transformed into exactly one primitive."
)
}
let backing_field = backing_field_primitives.into_iter().next().unwrap();
let selections_item = self.build_linked_field(context, client_edge_metadata.linked_field);
match &client_edge_metadata.metadata_directive {
ClientEdgeMetadataDirective::ClientObject {
model_resolvers, ..
} => {
let field_directives = match &client_edge_metadata.backing_field {
Selection::ScalarField(field) => Some(&field.directives),
// Although the reader checks for FragmentSpreads on the backing field, the normalization
// transforms inline the fragment spread so we match an InlineFragment here
Selection::InlineFragment(inline_frag) => Some(&inline_frag.directives),
_ => panic!(
"Expected Client Edge backing field to be a Relay Resolver. {:?}",View on GitHub (pinned to 668b1b85e0)
Solutions
- Ensure the client edge backing field is a single scalar field (or single inlined resolver fragment resolving to one field) with no sibling selections.
- Regenerate the client schema/extension so backing-field metadata matches the current document.
- Update the relay compiler if a transform recently changed how backing fields are normalized.
- Inspect the document with graphql-js validation/dump to see the actual backing field shape before compiling.
Example fix
// before: backing fragment with multiple fields
extend type Query { viewer: Viewer @clientEdge(backing: ViewerEdgeFragment) }
fragment ViewerEdgeFragment on Query { viewer node(id: $id) }
// after: single backing resolver field
extend type Query { viewer: Viewer @clientEdge }
fragment ViewerEdgeFragment on Query { viewer } Defensive patterns
Strategy: validation
Validate before calling
// Check client edge backing fragment has exactly one selection
function validateBackingFragment(fragment) {
if (!fragment || fragment.selections.length !== 1) {
throw new Error('Client edge backing fragment must contain exactly one selection, got ' +
(fragment ? fragment.selections.length : 0));
}
} Type guard
function isSingleSelectionBacking(fragment) {
return fragment != null &&
Array.isArray(fragment.selections) &&
fragment.selections.length === 1;
} Try / catch
try {
buildAst(context, clientEdgeMetadata);
} catch (e) {
if (String(e).includes('exactly one primitive')) {
console.error('Client edge backing field expanded to != 1 selection; inspect the backing fragment.');
} else throw e;
} Prevention
- Keep client edge backing fragments to a single scalar selection
- Regenerate client schema extensions after schema edits
- Pin relay compiler version across CI and local runs
- Add IR snapshots in tests for client edges
When it happens
Trigger: Compiling a @clientEdge/Relay resolver field whose backing_field selection builds to more or fewer than one primitive — e.g. the backing field is a fragment spread or inline fragment containing multiple selections instead of a single scalar/resolver field.
Common situations: Client schema extensions where the client edge backing field was edited to add extra fields; transforms that inlined fragments differently than expected; stale generated metadata after schema changes.
Related errors
- Expected Client Edge backing field to be a Relay Resolver. {
- Expected field backing a Client Edge to be a Relay Resolver.
- Expected a key
- unexpected value for @defer if argument: {other:?}
- unexpected value for @stream if argument: {other:?}
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/d23143f5058981a7.
Report an issue: GitHub.