{"record":{"id":"43a5f370f7a03491","repo":"facebook/relay","slug":"has-root-fragment-returned-true","errorCode":null,"errorMessage":"has_root_fragment() returned true","messagePattern":"has_root_fragment\\(\\) returned true","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/relay_resolvers/shadow_transform.rs","lineNumber":277,"sourceCode":"            self.errors.push(Diagnostic::error(\n                ValidationMessage::ReturnFragmentInvalidModuleName {\n                    module_name,\n                    fragment_name: return_fragment.item.0,\n                },\n                location,\n            ));\n        }\n\n        // Validate: return fragment must be spread within the root fragment.\n        //\n        // The `@returnFragment` placeholder spread is converted into the\n        // schema-known internal `@__relay_shadow_return` directive at the AST\n        // rail (before `build_ir`). So we look for that marker directive (keyed\n        // on the return fragment name), not the original spread, which no longer\n        // exists by the time we reach IR.\n        let root_fragment_name = field_metadata\n            .fragment_name\n            .expect(\"has_root_fragment() returned true\");\n\n        if let Some(root_fragment) = self.program.fragment(root_fragment_name)\n            && !selections_contain_shadow_return_marker(\n                &root_fragment.selections,\n                return_fragment.item,\n            )\n        {\n            self.errors.push(Diagnostic::error(\n                ValidationMessage::ReturnFragmentNotSpreadInRootFragment {\n                    return_fragment_name: return_fragment.item,\n                    root_fragment_name,\n                },\n                location,\n            ));\n        }\n    }\n}\n","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/relay_resolvers/shadow_transform.rs#L259-L295","documentation":"During resolver metadata validation, Relay's shadow transform expects that a field marked as having a root fragment (per has_root_fragment()) always carries a fragment_name in its metadata. The expect unwraps that Option; if absent, an earlier validation pass and the has_root_fragment computation disagreed. This is an internal invariant check, so hitting it indicates corrupted or inconsistently built resolver field metadata.","triggerScenarios":"validate_resolver_metadata processes a field whose RelayResolverFieldMetadata has has_root_fragment()==true but fragment_name==None — e.g. the @relay_resolver directive lacked fragment_name/root-fragment arguments, or custom metadata construction set the flag without the name.","commonSituations":"Hand-written or partially generated @relay_resolver directives missing the fragment_name argument; a mis-ordered transform pipeline running shadow_transform before metadata validation populates fragment_name; upgrading Relay with older persisted schema metadata.","solutions":["Ensure the @relay_resolver directive includes fragment_name (and required arguments) so has_root_fragment and fragment_name agree.","Re-run the full compiler pipeline from source rather than incremental artifacts so metadata is rebuilt consistently.","Check custom transforms that construct RelayResolverFieldMetadata and make them set fragment_name whenever the root-fragment flag is true.","File an issue with the failing document if the directive looks correct — it indicates an internal invariant bug."],"exampleFix":"// before\n@relay_resolver(import_path: \"ImageResolver\") // no fragment_name\n// after\n@relay_resolver(import_path: \"ImageResolver\", fragment_name: \"ImageResolverFragment\", root_fragment_name: \"ImageResolverRootFragment\")","handlingStrategy":"validation","validationCode":"// Pre-check resolver directive metadata coherence before compiling:\nif directive.args.get(\"has_root_fragment\") == Some(&Bool(true)) {\n    assert!(directive.args.contains_key(\"fragment_name\"), \"@relay_resolver with root fragment requires fragment_name\");\n}","typeGuard":"fn metadata_is_coherent(meta: &RelayResolverFieldMetadata) -> bool {\n    !(meta.has_root_fragment() && meta.fragment_name.is_none())\n}","tryCatchPattern":"// Panics are not catchable as errors; pre-validate:\nlet meta = RelayResolverFieldMetadata::find(directives).ok_or(\"not a resolver field\")?;\nif !metadata_is_coherent(&meta) {\n    return Err(anyhow!(\"resolver metadata: has_root_fragment set without fragment_name\"));\n}","preventionTips":["Always supply fragment_name and root_fragment_name on @relay_resolver directives that use root fragments","Rebuild from clean state after upgrading Relay so old metadata doesn't linger","Avoid custom transforms that mutate RelayResolverFieldMetadata flags independently","If the directive looks correct, report it upstream as a compiler invariant bug"],"tags":["relay","compiler-transform","panic","resolver-metadata","invariant"],"backgroundTag":"resolver-metadata-invariant-violation","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}