{"record":{"id":"4145cff0f5597883","repo":"facebook/relay","slug":"expected-field-to-be-defined-on-concrete-type-4145cf","errorCode":null,"errorMessage":"Expected field to be defined on concrete type","messagePattern":"Expected field to be defined on concrete type","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs","lineNumber":157,"sourceCode":"            .iter()\n            .find(|directive| directive.name.0 == RELAY_RESOLVER_DIRECTIVE_NAME.0)\n            && resolver_directive\n                .arguments\n                .named(ArgumentName(*ROOT_FRAGMENT_FIELD))\n                .is_some()\n        {\n            return true;\n        }\n        // Any of the implementing objects' corresponding field is a resolver field\n        let selection_name = interface_field.name.item;\n        let implementing_objects =\n            interface.recursively_implementing_objects(Arc::as_ref(&self.program.schema));\n        implementing_objects.iter().any(|object_id| {\n            let concrete_field_id = self\n                .program\n                .schema\n                .named_field(Type::Object(*object_id), selection_name)\n                .expect(\"Expected field to be defined on concrete type\");\n            let concrete_field = self.program.schema.field(concrete_field_id);\n            // A field is a \"different implementation\" if it's either an explicit\n            // resolver or any extension field (e.g. a synthetic ID field on a\n            // client model type that has no @relay_resolver directive but is still\n            // client-only and must be fanned out per concrete type).\n            concrete_field.is_extension\n                || concrete_field\n                    .directives\n                    .iter()\n                    .any(|directive| directive.name.0 == RELAY_RESOLVER_DIRECTIVE_NAME.0)\n        })\n    }\n\n    fn create_inline_fragment_selections_for_interface(\n        &self,\n        interface_id: InterfaceID,\n        selections: &[Selection],\n    ) -> Vec<Selection> {","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet concrete_field_id = self.program.schema\n    .named_field(Type::Object(*object_id), selection_name)\n    .expect(\"Expected field to be defined on concrete type\");\n// after\nlet Some(concrete_field_id) = self.program.schema\n    .named_field(Type::Object(*object_id), selection_name) else { return false; };","handlingStrategy":"type-guard","validationCode":"for object_id in interface.recursively_implementing_objects(schema) {\n    assert!(schema.named_field(Type::Object(object_id), field_name).is_some(),\n        \"{field_name} must be defined on implementing object\");\n}","typeGuard":"fn field_on_all_impls(schema: &Schema, iface: InterfaceId, name: &str) -> bool {\n    iface.recursively_implementing_objects(schema).iter().all(|o| schema.named_field(Type::Object(*o), name).is_some())\n}","tryCatchPattern":"let Some(field_id) = schema.named_field(Type::Object(*object_id), selection_name) else { return false; };","preventionTips":["Always define interface fields on every implementing object.","Run schema validation before IR transforms.","Watch client-extension generation for missing fields."],"tags":["relay-compiler","schema","interface"],"backgroundTag":"schema-field-not-on-concrete-type","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}