{"record":{"id":"40c8b43eba74a5b3","repo":"facebook/relay","slug":"unexpected-relayresolvermetadata-on-fragment-sprea","errorCode":null,"errorMessage":"Unexpected RelayResolverMetadata on fragment spread while generating normalization AST.","messagePattern":"Unexpected RelayResolverMetadata on fragment spread while generating normalization AST\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-codegen/src/build_ast.rs","lineNumber":1536,"sourceCode":"                            definition: WithLocation::generated(resolver_metadata.field_id),\n                            arguments: resolver_metadata.field_arguments.clone(),\n                            directives: frag_spread\n                                .directives\n                                .iter()\n                                .filter(|directive| {\n                                    directive.name.item\n                                        != RequiredMetadataDirective::directive_name()\n                                })\n                                .cloned()\n                                .collect(),\n                        };\n                        self.build_scalar_field(&scalar_field)\n                    } else {\n                        self.build_reader_relay_resolver(resolver_metadata, Some(primitive))\n                    }\n                }\n                // We expect all RelayResolver fragment spreads to be inlined into inline fragment spreads when generating Normalization ASTs.\n                CodegenVariant::Normalization => panic!(\n                    \"Unexpected RelayResolverMetadata on fragment spread while generating normalization AST.\"\n                ),\n            };\n\n            if let Some(required_metadata) =\n                RequiredMetadataDirective::find(&frag_spread.directives)\n            {\n                self.build_required_field(required_metadata, resolver_primitive)\n            } else if let Some(catch_metadata) =\n                CatchMetadataDirective::find(&frag_spread.directives)\n            {\n                self.build_catch_node(catch_metadata, resolver_primitive)\n            } else {\n                resolver_primitive\n            }\n        } else {\n            primitive\n        }","sourceCodeStart":1518,"sourceCodeEnd":1554,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-codegen/src/build_ast.rs#L1518-L1554","documentation":"RelayResolver metadata on a plain fragment spread is only supported when generating Reader ASTs. When generating Normalization ASTs, the compiler expects all resolver fragment spreads to have been inlined into inline fragments, so encountering one panics.","triggerScenarios":"A fragment spread (`...SomeResolverFragment`) whose directives contain RelayResolverMetadata is processed by build_fragment_spread while CodegenVariant::Normalization is active (reached via build_reader_client_edge callers' normalization counterpart).","commonSituations":"Hand-writing a fragment spread with @relay_resolver-style metadata instead of relying on generated inline fragments; using a custom/partial compile step that skips the inlining pass; feeding documents compiled by a different relay version into normalization codegen.","solutions":["Let the relay compiler pipeline generate the resolver fragment usage (inline fragment form) rather than hand-writing spreads with resolver directives.","Remove resolver metadata directives from the fragment spread if it isn't a resolver, and reference the fragment normally.","Run normalization codegen only on documents that have gone through the client-edge inlining pass; fix custom build scripts to include that pass.","Align relay compiler versions across steps so normalization codegen matches the document format produced upstream."],"exampleFix":"// before\n... on Query {\n  ...MyResolverFragment @relay_resolver\n}\n// after: use the compiler-generated inline fragment form\n... on Query {\n  ... on Query @inline @refetchable(queryName: \"MyResolverQuery\") { ... }\n}\n","handlingStrategy":"validation","validationCode":"fn spread_is_normalization_safe(spread: &FragmentSpread) -> bool {\n    RelayResolverMetadata::find(&spread.directives).is_none()\n}\n","typeGuard":"fn spread_has_resolver_metadata(spread: &FragmentSpread) -> bool {\n    RelayResolverMetadata::find(&spread.directives).is_some()\n}\n","tryCatchPattern":"let result = std::panic::catch_unwind(|| build_fragment_spread(...));\nif result.is_err() { report(\"resolver fragment spread reached normalization codegen; run inlining pass first\"); }\n","preventionTips":["Run the client-edge inlining pass before normalization codegen.","Don't hand-write @relay_resolver metadata on fragment spreads; use generated inline fragments.","Keep relay compiler versions consistent across all compilation steps."],"tags":["graphql","resolver","fragment-spread","normalization"],"backgroundTag":"unexpected-resolver-metadata","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}