{"record":{"id":"764d8e90eed55a4b","repo":"facebook/relay","slug":"expect-the-module-import-inline-fragment-to-have-a","errorCode":null,"errorMessage":"Expect the module import inline fragment to have a type","messagePattern":"Expect the module import inline fragment to have a type","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/match_/split_module_import.rs","lineNumber":114,"sourceCode":"        }\n    }\n\n    fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed<Selection> {\n        if let Some(module_metadata) = self.inline_module_metadata(fragment) {\n            // We do not need to write normalization files for base fragments.\n            // This is because when we process the base project, the normalization fragment will\n            // be written, and we do not want to emit multiple normalization fragments with\n            // the same name. If we did, Haste would complain about a duplicate module definition.\n            if self\n                .base_fragment_names\n                .contains(&module_metadata.fragment_name)\n            {\n                return self.default_transform_inline_fragment(fragment);\n            }\n\n            let parent_type = fragment\n                .type_condition\n                .expect(\"Expect the module import inline fragment to have a type\");\n\n            let normalization_name =\n                get_normalization_operation_name(module_metadata.fragment_name.0).intern();\n            let schema = &self.program.schema;\n            let created_split_operation = self\n                .split_operations\n                .entry(normalization_name)\n                .or_insert_with(|| {\n                    // Exclude `__module_operation/__module_component: js` field selections from `SplitOperation`\n                    let next_selections = fragment\n                        .selections\n                        .iter()\n                        .filter(|selection| match selection {\n                            Selection::ScalarField(field) => {\n                                field.alias.is_none()\n                                    || schema.field(field.definition.item).name.item\n                                        != MATCH_CONSTANTS.js_field_name\n                            }","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/match_/split_module_import.rs#L96-L132","documentation":"split_module_import's transform_inline_fragment only processes inline fragments identified as module imports; after confirming it is one, it force-unwraps the fragment's type_condition. The expect fires when a module-import inline fragment has no type condition (e.g. ... @module { ... } with no 'on Type'), since the split/normalization operation requires it.","triggerScenarios":"An inline fragment annotated for module import (matched via module_metadata) lacking a type condition, so fragment.type_condition is None when computing get_normalization_operation_name and building the split operation.","commonSituations":"Hand-written fragments using ... @module without 'on Type'; a code generator that dropped the type condition; refactors removing the type condition assuming it can be inferred from the parent type; plugins emitting typeless inline fragments.","solutions":["Add an explicit type condition to the module import inline fragment: ... on TypeName @module(name: \"...\") { ... }.","Fix the generator/transform that produced the fragment so it always emits a type condition.","Confirm the fragment really is a module import — plain inline fragments without a type condition safely take the default_transform_inline_fragment path.","Re-run codegen after fixing so the split operations are regenerated.","If the syntax looks valid, minimize the document and report a relay-compiler issue."],"exampleFix":"// before\nfragment F on Query {\n  ... @module(name: \"ProfilePhoto\") { photo }\n}\n// after\nfragment F on Query {\n  ... on User @module(name: \"ProfilePhoto\") { photo }\n}","handlingStrategy":"type-guard","validationCode":"function validateModuleImportFragment(fragment) {\n  if (!hasModuleDirective(fragment)) return true;\n  return Boolean(\n    fragment.typeCondition && fragment.typeCondition.name?.value\n  );\n}\n// before compiling: if (!validateModuleImportFragment(frag)) throw new Error('@module inline fragment needs \"on Type\"');","typeGuard":"function hasTypeCondition(fragment) {\n  return fragment.kind === 'InlineFragment' &&\n    fragment.typeCondition != null &&\n    typeof fragment.typeCondition.name?.value === 'string';\n}","tryCatchPattern":"try {\n  program = applySplitModuleImport(program);\n} catch (e) {\n  if (String(e.message).includes('module import inline fragment to have a type')) {\n    reportDocumentError(programName, 'Module-import inline fragment is missing its \"on TypeName\" condition');\n  }\n  throw e;\n}","preventionTips":["Always write ... on TypeName @module(name: \"...\") — never a bare ... @module.","Add a lint rule requiring typeCondition on inline fragments carrying @module.","Fix generators/plugins to emit type conditions for module-import fragments.","Re-run codegen after any fragment refactor so split operations stay in sync."],"tags":["relay-compiler","graphql","inline-fragment","match-transform","missing-type-condition","panic"],"backgroundTag":"missing-fragment-type-condition","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}