facebook/relay · error
Expected field to be defined on concrete type
Error message
Expected field to be defined on concrete type
What it means
In concrete_types_have_different_implementations, when deciding whether to fan out an interface field's selections per concrete object type, the code looks up the selection's field on each implementing object via schema.named_field(...) and expects it to exist on every concrete type. This assumes schema validation previously proved the field is defined on all implementing objects; if an implementing object lacks the field, the expect panics.
Source
Thrown at compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs:157
.iter()
.find(|directive| directive.name.0 == RELAY_RESOLVER_DIRECTIVE_NAME.0)
&& resolver_directive
.arguments
.named(ArgumentName(*ROOT_FRAGMENT_FIELD))
.is_some()
{
return true;
}
// Any of the implementing objects' corresponding field is a resolver field
let selection_name = interface_field.name.item;
let implementing_objects =
interface.recursively_implementing_objects(Arc::as_ref(&self.program.schema));
implementing_objects.iter().any(|object_id| {
let concrete_field_id = self
.program
.schema
.named_field(Type::Object(*object_id), selection_name)
.expect("Expected field to be defined on concrete type");
let concrete_field = self.program.schema.field(concrete_field_id);
// A field is a "different implementation" if it's either an explicit
// resolver or any extension field (e.g. a synthetic ID field on a
// client model type that has no @relay_resolver directive but is still
// client-only and must be fanned out per concrete type).
concrete_field.is_extension
|| concrete_field
.directives
.iter()
.any(|directive| directive.name.0 == RELAY_RESOLVER_DIRECTIVE_NAME.0)
})
}
fn create_inline_fragment_selections_for_interface(
&self,
interface_id: InterfaceID,
selections: &[Selection],
) -> Vec<Selection> {View on GitHub (pinned to 668b1b85e0)
Solutions
- Fix the schema so every object implementing the interface defines the selected field (interface field implementations are mandatory).
- Run/enable the schema validation passes before relay_resolvers_abstract_types.
- Check custom client-extension generation for object types missing the field.
- If the field genuinely is absent, guard with if let Some(...) instead of expect in a local fork and skip the object.
Example fix
// before
let concrete_field_id = self.program.schema
.named_field(Type::Object(*object_id), selection_name)
.expect("Expected field to be defined on concrete type");
// after
let Some(concrete_field_id) = self.program.schema
.named_field(Type::Object(*object_id), selection_name) else { return false; }; Defensive patterns
Strategy: type-guard
Validate before calling
for object_id in interface.recursively_implementing_objects(schema) {
assert!(schema.named_field(Type::Object(object_id), field_name).is_some(),
"{field_name} must be defined on implementing object");
} Type guard
fn field_on_all_impls(schema: &Schema, iface: InterfaceId, name: &str) -> bool {
iface.recursively_implementing_objects(schema).iter().all(|o| schema.named_field(Type::Object(*o), name).is_some())
} Try / catch
let Some(field_id) = schema.named_field(Type::Object(*object_id), selection_name) else { return false; }; Prevention
- Always define interface fields on every implementing object.
- Run schema validation before IR transforms.
- Watch client-extension generation for missing fields.
When it happens
Trigger: An interface field selection whose name is not defined on one of the interface's recursively-implementing objects reaches should_copy_selection — e.g. the schema was built with a field defined on the interface but a concrete object misses it, bypassing the normal validation pass.
Common situations: Hand-constructed or extended schemas (client extensions / synthetic ID fields) where an implementing object type omits the interface field; running the abstract-types transform before schema validation; schema stitching that dropped a field.
Related errors
- Expected presence of edges field to have been previously val
- Expected presence of page_info field to have been previously
- commitMutation: Expected mutation operation
- commitMutation: Expected mutation operation
- commitMutation: Expected mutation to be of type request
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/4145cff0f5597883.
Report an issue: GitHub.