facebook/relay · error
@module fragments should be named 'FragmentName_propName', g
Error message
@module fragments should be named 'FragmentName_propName', got '{fragment_name}'. What it means
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.
Source
Thrown at compiler/crates/relay-codegen/src/build_ast.rs:2751
let artifact_path = self
.project_config
.artifact_path_for_definition(self.definition_source_location);
let norm_artifact_path = self
.project_config
.path_for_language_specific_artifact(fragment_source_location, normalization_filename);
self.project_config
.js_module_import_identifier(&artifact_path, &norm_artifact_path)
}
fn build_module_import_selections(
&mut self,
module_metadata: &ModuleMetadata,
inline_fragment: &InlineFragment,
) -> Vec<Primitive> {
let fragment_name = module_metadata.fragment_name;
let fragment_name_str = fragment_name.0.lookup();
let underscore_idx = fragment_name_str.find('_').unwrap_or_else(|| {
panic!(
"@module fragments should be named 'FragmentName_propName', got '{fragment_name}'."
)
});
let frag_spread = inline_fragment.selections.iter().find_map(|sel| match sel {
Selection::FragmentSpread(frag_spread) => Some(frag_spread),
_ => None,
});
let args = if let Some(frag_spread) = frag_spread {
self.build_arguments(&frag_spread.arguments)
} else {
None
};
let mut module_import = object! {
args: match args {
None => Primitive::SkippableNull,
Some(key) => Primitive::Key(key),
},View on GitHub (pinned to 668b1b85e0)
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
Example fix
// before
fragment AvatarFragment on User @module(as: "AvatarFragment") { ... }
// after
fragment AvatarFragment_user on User @module(as: "AvatarFragment_user") { ... } Defensive patterns
Strategy: validation
Validate before calling
function isValidModuleFragmentName(name) {
return typeof name === 'string' && name.includes('_') && /^FragmentName_propName$/.test(name.replace(/^[^_]+_[^_]+$/, 'FragmentName_propName'));
}
if (!isValidModuleFragmentName(fragmentName)) {
throw new Error(`@module fragment must be named 'FragmentName_propName', got '${fragmentName}'`);
} Type guard
const isModuleFragmentName = (name) =>
typeof name === 'string' && name.indexOf('_') !== -1; Try / catch
try {
compiled = compile(queryText);
} catch (e) {
if (String(e).includes("@module fragments should be named")) {
// fix the fragment name to 'FragmentName_propName'
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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`).
Common situations: 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.
Related errors
- Expected Condition with static value to have been pruned or
- The {} argument in exec_time_resolvers directive should be t
- Unexpected RelayResolverMetadata on inline fragment while ge
- Unexpected parent type for resolver.
- Expected at most one handle directive, got `{handle_field_di
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/7caa46e6e37e8bc8.
Report an issue: GitHub.