{"record":{"id":"631044c6fd135976","repo":"facebook/relay","slug":"expected-a-key","errorCode":null,"errorMessage":"Expected a key","messagePattern":"Expected a key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-codegen/src/ast.rs","lineNumber":146,"sourceCode":"        resolver_fn: Box<Primitive>,\n        injected_field_name_details: Option<(StringKey, bool)>,\n    },\n}\n\nimpl Primitive {\n    pub fn assert_string(&self) -> StringKey {\n        if let Primitive::String(key) = self {\n            *key\n        } else {\n            panic!(\"Expected a string\");\n        }\n    }\n\n    pub fn assert_key(&self) -> AstKey {\n        if let Primitive::Key(key) = self {\n            *key\n        } else {\n            panic!(\"Expected a key\");\n        }\n    }\n\n    pub fn string_or_null(str: Option<StringKey>) -> Primitive {\n        match str {\n            None => Primitive::Null,\n            Some(str) => Primitive::String(str),\n        }\n    }\n}\n\n#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]\npub struct AstKey(usize);\n\nimpl AstKey {\n    pub fn as_usize(self) -> usize {\n        self.0\n    }","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-codegen/src/ast.rs#L128-L164","documentation":"Primitive::assert_key unwraps a Primitive enum expecting the Key variant. The relay-codegen AST can wrap either a scalar value or a client-object key; calling assert_key on a Primitive that is String/Boolean/Null/etc. panics because no key exists to return.","triggerScenarios":"Codegen logic calls primitive.assert_key() on a Primitive produced by Primitive::string/string_or_null/boolean rather than from a key-valued AST node — e.g. reading a field whose value was generated as a plain string literal.","commonSituations":"Custom codegen plugins/extensions walking the generated AST and assuming every primitive is a key; a schema/config change causing a field that used to be @refetchable/client-edge linked to become a plain string, so the primitive variant changed.","solutions":["Check the Primitive variant with matches! or a match arm before calling assert_key, and handle non-key variants gracefully.","Trace why the AST node holds a non-key value: verify the field/fragment is actually a client edge (@refetchable / @catch) so codegen emits a Key.","Fix the calling codegen pass to call the correct accessor (e.g. assert_string) for the actual variant.","Pin/upgrade relay compiler so the pass producing the Primitive matches the pass consuming it."],"exampleFix":"// before\nlet key = primitive.assert_key();\n// after\nlet key = match primitive {\n    Primitive::Key(key) => key,\n    other => panic!(\"expected key, got {:?}\", other),\n};\n","handlingStrategy":"type-guard","validationCode":"if matches!(primitive, Primitive::Key(_)) { /* safe to assert_key */ }\n","typeGuard":"fn is_key(p: &Primitive) -> bool { matches!(p, Primitive::Key(_)) }\n","tryCatchPattern":"// Rust panics are not catchable per-call; wrap the whole codegen step\nlet result = std::panic::catch_unwind(|| primitive.assert_key());\nmatch result {\n    Ok(key) => use_key(key),\n    Err(_) => fallback_to_non_key_handling(),\n}\n","preventionTips":["Match on the Primitive enum instead of calling assert_* unwraps in plugin code.","Verify client-edge fields actually produce Key primitives before consuming them.","Keep producer and consumer codegen passes on the same compiler version."],"tags":["rust","codegen","panic","enum-variant"],"backgroundTag":"unexpected-enum-variant","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}