{"record":{"id":"bd262b5fc6338a2f","repo":"facebook/relay","slug":"shadow-return-directive-fragment-name-matched-the","errorCode":null,"errorMessage":"shadow_return_directive_fragment_name matched the directive","messagePattern":"shadow_return_directive_fragment_name matched the directive","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/relay_resolvers/shadow_transform.rs","lineNumber":385,"sourceCode":"    }\n\n    fn transform_linked_field(\n        &mut self,\n        field: &graphql_ir::LinkedField,\n    ) -> Transformed<graphql_ir::Selection> {\n        // Check for RelayResolverFieldMetadata attached by field_transform\n        if let Some(field_metadata) = RelayResolverFieldMetadata::find(&field.directives) {\n            self.validate_resolver_metadata(field_metadata, field.definition.item);\n        }\n\n        // Convert the schema-known `@__relay_shadow_return` syntax directive into\n        // the typed associated-data marker and strip the syntax directive so it\n        // never reaches codegen.\n        if let Some(return_fragment_name) = shadow_return_directive_fragment_name(field) {\n            let shadow_return_directive = field\n                .directives\n                .named(*SHADOW_RETURN_DIRECTIVE_NAME)\n                .expect(\"shadow_return_directive_fragment_name matched the directive\");\n            let marker = ShadowReturnMarker {\n                return_fragment_name,\n                spread_location: shadow_return_directive.location,\n            };\n            let mut directives: Vec<_> = field\n                .directives\n                .iter()\n                .filter(|directive| directive.name.item != *SHADOW_RETURN_DIRECTIVE_NAME)\n                .cloned()\n                .collect();\n            directives.push(marker.into());\n            let selections = self\n                .transform_selections(&field.selections)\n                .replace_or_else(|| field.selections.clone());\n            return Transformed::Replace(Selection::LinkedField(Arc::new(LinkedField {\n                directives,\n                selections,\n                ..field.clone()","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/relay_resolvers/shadow_transform.rs#L367-L403","documentation":"In transform_linked_field, shadow_return_directive_fragment_name(field) returned Some, meaning the field carries the shadow return directive, but the subsequent directives.named(SHADOW_RETURN_DIRECTIVE_NAME).expect(...) lookup found nothing. The two lookups should be consistent — they both key on the same directive — so a mismatch indicates duplicate/inconsistent directive name constants or a race where directives were mutated between checks.","triggerScenarios":"A field for which shadow_return_directive_fragment_name matches (via the return-fragment directive name) but directives.named with the separate SHADOW_RETURN_DIRECTIVE_NAME constant finds no match — e.g. mismatched directive name constants after a refactor, or a parallel transform stripped the directive between the two calls.","commonSituations":"Custom or forked Relay builds where directive name constants were edited inconsistently; transform pipelines inserting an extra pass that rewrites/removes field directives; copy-pasted helper that checks a different directive name than the one fetched.","solutions":["Verify shadow_return_directive_fragment_name and the expect() use the identical directive-name constant; align them.","Re-run from a clean build so no stale incremental transform output mutates directives.","Audit other transforms for anything that strips directives named like the shadow return marker and reorder passes.","Use Option combinators instead of expect to surface a diagnostic message if you control the fork."],"exampleFix":"// before\n.expect(\"shadow_return_directive_fragment_name matched the directive\")\n// after\nlet shadow_return_directive = field.directives.named(*SHADOW_RETURN_DIRECTIVE_NAME)\n    .unwrap_or_else(|| panic!(\"field {:?} reported shadow-return fragment {:?} but directive missing\", field.name, return_fragment_name));","handlingStrategy":"validation","validationCode":"// Confirm directive presence with one constant before transforming:\nlet directive_name = *SHADOW_RETURN_DIRECTIVE_NAME;\nif shadow_return_directive_fragment_name(field).is_some() {\n    assert!(field.directives.named(directive_name).is_some(),\n        \"field {} claims shadow-return fragment but lacks the directive\", field.name.item.0);\n}","typeGuard":"fn has_shadow_return_directive(field: &LinkedField) -> bool {\n    shadow_return_directive_fragment_name(field).is_some()\n        && field.directives.named(*SHADOW_RETURN_DIRECTIVE_NAME).is_some()\n}","tryCatchPattern":"// Prefer graceful handling over expect when forking:\nmatch field.directives.named(*SHADOW_RETURN_DIRECTIVE_NAME) {\n    Some(d) => { /* build ShadowReturnMarker */ }\n    None => diagnostics.add(Diagnostic::error(field.location, \"shadow return marker directive missing\")),\n}","preventionTips":["Use a single shared constant for the directive name everywhere — never re-declare it in helper functions","Rebuild cleanly after pulling Relay updates to avoid mixed transform versions","Audit any custom transform that rewrites field.directives for accidental stripping","Add a unit test pairing shadow_return_directive_fragment_name with directive lookup"],"tags":["relay","compiler-transform","panic","directive","invariant"],"backgroundTag":"shadow-return-directive-mismatch","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}