{"record":{"id":"4d3cad297ea0d403","repo":"facebook/relay","slug":"expected-field-backing-a-client-edge-to-be-a-relay","errorCode":null,"errorMessage":"Expected field backing a Client Edge to be a Relay Resolver. {field:?}","messagePattern":"Expected field backing a Client Edge to be a Relay Resolver\\. (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-codegen/src/build_ast.rs","lineNumber":2167,"sourceCode":"    }\n\n    fn build_reader_client_edge(\n        &mut self,\n        context: &mut ContextualMetadata,\n        client_edge_metadata: &ClientEdgeMetadata<'_>,\n        required_metadata: Option<RequiredMetadataDirective>,\n        catch_metadata: Option<CatchMetadataDirective>,\n    ) -> Primitive {\n        context.has_client_edges = true;\n        let backing_field = match &client_edge_metadata.backing_field {\n            Selection::FragmentSpread(fragment_spread) => {\n                self.build_fragment_spread(fragment_spread)\n            }\n            Selection::ScalarField(field) => {\n                if let Some(resolver_metadata) = RelayResolverMetadata::find(&field.directives) {\n                    self.build_scalar_backed_resolver_field(context, field, resolver_metadata)\n                } else {\n                    panic!(\"Expected field backing a Client Edge to be a Relay Resolver. {field:?}\")\n                }\n            }\n            _ => panic!(\n                \"Expected Client Edge backing field to be a Relay Resolver. {:?}\",\n                client_edge_metadata.backing_field\n            ),\n        };\n\n        let selections_item = if required_metadata.is_none() {\n            self.build_linked_field(context, client_edge_metadata.linked_field)\n        } else {\n            let next_directives = client_edge_metadata\n                .linked_field\n                .directives\n                .iter()\n                .filter(|directive| {\n                    directive.name.item != RequiredMetadataDirective::directive_name()\n                })","sourceCodeStart":2149,"sourceCodeEnd":2185,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-codegen/src/build_ast.rs#L2149-L2185","documentation":"In the client edge build path for scalar-backed fields, the compiler looks up Relay Resolver metadata (@relay_resolver-style directives) on the scalar field backing a client edge. If the field is a ScalarField but carries no resolver metadata, the compiler cannot treat it as a client edge backing field and panics, printing the field.","triggerScenarios":"A client edge whose backing scalar field lacks RelayResolverMetadata directives — e.g. the @relay_resolver directive was removed, renamed, or never added while the field is still wired as a client edge backing field.","commonSituations":"Removing a resolver directive during refactoring while leaving client edge wiring; schema extensions generated by older tooling without resolver annotations; directives stripped by an earlier transform.","solutions":["Add the Relay Resolver directive (e.g. @relay_resolver with root_fragment_name) to the backing scalar field.","If the field is not a resolver, change the client edge to point at the correct resolver-backed field.","Check that no custom transform strips the resolver directives before codegen.","Validate the client schema extension with the relay compiler's schema checks before compiling documents."],"exampleFix":"// before\nextend type Query { myEdge: MyType @clientEdge }\n\n// after\nextend type Query { myEdge: MyType @relay_resolver(root_fragment_name: \"MyEdgeFragment\") @clientEdge }","handlingStrategy":"validation","validationCode":"function validateResolverDirective(field) {\n  const hasResolver = field.directives?.some(d =>\n    ['relay_resolver', 'relayResolver'].includes(d.name.value));\n  if (!hasResolver) {\n    throw new Error('Field ' + field.name.value + ' backs a client edge but has no @relay_resolver directive');\n  }\n}","typeGuard":"function isRelayResolverField(field) {\n  return field?.directives?.some(d => d.name.value === 'relay_resolver') === true;\n}","tryCatchPattern":"try {\n  compile();\n} catch (e) {\n  if (String(e).includes('backing a Client Edge to be a Relay Resolver')) {\n    console.error('Add @relay_resolver to the backing field:', e.message);\n  } else throw e;\n}","preventionTips":["Always annotate client edge backing scalars with @relay_resolver","Grep schema extensions for client edges missing resolver directives before compiling","Avoid custom transforms that strip resolver directives","Validate client schema extensions in CI"],"tags":["relay-compiler","client-edge","relay-resolver","panic"],"backgroundTag":"invalid-client-edge-definition","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}