{"record":{"id":"7caa46e6e37e8bc8","repo":"facebook/relay","slug":"module-fragments-should-be-named-fragmentname-pr","errorCode":null,"errorMessage":"@module fragments should be named 'FragmentName_propName', got '{fragment_name}'.","messagePattern":"@module fragments should be named 'FragmentName_propName', got '(.+?)'\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-codegen/src/build_ast.rs","lineNumber":2751,"sourceCode":"        let artifact_path = self\n            .project_config\n            .artifact_path_for_definition(self.definition_source_location);\n        let norm_artifact_path = self\n            .project_config\n            .path_for_language_specific_artifact(fragment_source_location, normalization_filename);\n        self.project_config\n            .js_module_import_identifier(&artifact_path, &norm_artifact_path)\n    }\n\n    fn build_module_import_selections(\n        &mut self,\n        module_metadata: &ModuleMetadata,\n        inline_fragment: &InlineFragment,\n    ) -> Vec<Primitive> {\n        let fragment_name = module_metadata.fragment_name;\n        let fragment_name_str = fragment_name.0.lookup();\n        let underscore_idx = fragment_name_str.find('_').unwrap_or_else(|| {\n            panic!(\n                \"@module fragments should be named 'FragmentName_propName', got '{fragment_name}'.\"\n            )\n        });\n\n        let frag_spread = inline_fragment.selections.iter().find_map(|sel| match sel {\n            Selection::FragmentSpread(frag_spread) => Some(frag_spread),\n            _ => None,\n        });\n        let args = if let Some(frag_spread) = frag_spread {\n            self.build_arguments(&frag_spread.arguments)\n        } else {\n            None\n        };\n        let mut module_import = object! {\n            args: match args {\n                None => Primitive::SkippableNull,\n                Some(key) => Primitive::Key(key),\n            },","sourceCodeStart":2733,"sourceCodeEnd":2769,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-codegen/src/build_ast.rs#L2733-L2769","documentation":"Relay's @module directive (used with @match) requires the generated fragment to be named 'FragmentName_propName' so the builder can split the name on the first underscore to recover the property name and the fragment spread. A fragment name with no underscore cannot be decomposed, so codegen panics instead of emitting a module. This is a naming-contract enforcement for @module/@match serialization.","triggerScenarios":"Using @module on an inline fragment whose associated fragment name lacks an underscore, e.g. `... on Post @module(as: \"UserFragment\")` or naming the fragment without the `_propName` suffix (e.g. `AvatarFragment` instead of `AvatarFragment_user`).","commonSituations":"Developers adopting @match/@module for the first time and naming fragments per ordinary conventions; renaming a module fragment without keeping the `FragmentName_propName` suffix; codegen after upgrading Relay that now enforces the stricter contract.","solutions":["Rename the module fragment to include the property suffix, e.g. `AvatarFragment_user` where `user` is the property the module populates","Ensure the fragment name referenced by @module `as:` matches `<FragmentName>_<propName>` with at least one underscore","Check the inline fragment selections spread a fragment whose name follows the same convention","Update @match usages generated by older tooling to the current naming convention"],"exampleFix":"// before\nfragment AvatarFragment on User @module(as: \"AvatarFragment\") { ... }\n// after\nfragment AvatarFragment_user on User @module(as: \"AvatarFragment_user\") { ... }","handlingStrategy":"validation","validationCode":"function isValidModuleFragmentName(name) {\n  return typeof name === 'string' && name.includes('_') && /^FragmentName_propName$/.test(name.replace(/^[^_]+_[^_]+$/, 'FragmentName_propName'));\n}\nif (!isValidModuleFragmentName(fragmentName)) {\n  throw new Error(`@module fragment must be named 'FragmentName_propName', got '${fragmentName}'`);\n}","typeGuard":"const isModuleFragmentName = (name) =>\n  typeof name === 'string' && name.indexOf('_') !== -1;","tryCatchPattern":"try {\n  compiled = compile(queryText);\n} catch (e) {\n  if (String(e).includes(\"@module fragments should be named\")) {\n    // fix the fragment name to 'FragmentName_propName'\n  }\n  throw e;\n}","preventionTips":["Name every @module fragment as FragmentName_propName","Add a lint rule enforcing an underscore in fragments used with @module","When renaming fragments, keep the _propName suffix intact","Verify @module/@match fixtures compile in CI"],"tags":["compiler","graphql","codegen","relay-match","module-directive"],"backgroundTag":"invalid-module-fragment-name","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}