facebook/relay · error
Expected a key
Error message
Expected a key
What it means
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.
Source
Thrown at compiler/crates/relay-codegen/src/ast.rs:146
resolver_fn: Box<Primitive>,
injected_field_name_details: Option<(StringKey, bool)>,
},
}
impl Primitive {
pub fn assert_string(&self) -> StringKey {
if let Primitive::String(key) = self {
*key
} else {
panic!("Expected a string");
}
}
pub fn assert_key(&self) -> AstKey {
if let Primitive::Key(key) = self {
*key
} else {
panic!("Expected a key");
}
}
pub fn string_or_null(str: Option<StringKey>) -> Primitive {
match str {
None => Primitive::Null,
Some(str) => Primitive::String(str),
}
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub struct AstKey(usize);
impl AstKey {
pub fn as_usize(self) -> usize {
self.0
}View on GitHub (pinned to 668b1b85e0)
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.
Example fix
// before
let key = primitive.assert_key();
// after
let key = match primitive {
Primitive::Key(key) => key,
other => panic!("expected key, got {:?}", other),
};
Defensive patterns
Strategy: type-guard
Validate before calling
if matches!(primitive, Primitive::Key(_)) { /* safe to assert_key */ }
Type guard
fn is_key(p: &Primitive) -> bool { matches!(p, Primitive::Key(_)) }
Try / catch
// Rust panics are not catchable per-call; wrap the whole codegen step
let result = std::panic::catch_unwind(|| primitive.assert_key());
match result {
Ok(key) => use_key(key),
Err(_) => fallback_to_non_key_handling(),
}
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected DynamicImport
- Unexpected RelayResolver
- Expected at least one of an @updatable reader AST, or normal
- Expected to have a reader operation for `{normal_name}`
- Expected to have a typegen operation for `{normal_name}`
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/631044c6fd135976.
Report an issue: GitHub.