facebook/relay · error
Unexpected GraphQLModuleDependency
Error message
Unexpected GraphQLModuleDependency
What it means
Primitive::GraphQLModuleDependency wraps a @module/@match module dependency and is consumed by dedicated module-import writers, not by write_constant_value. If such a dependency appears where a plain constant value is being printed (argument literals, lists, object values), the printer panics because it cannot serialize a module reference as a literal.
Source
Thrown at compiler/crates/relay-codegen/src/printer.rs:932
write!(f, "\\\"{name}\\\":")?;
write_constant_value(f, builder, value)?;
f.push(',');
}
if !obj.is_empty() {
f.pop();
}
f.push('}');
Ok(())
}
}
}
Primitive::Null | Primitive::SkippableNull => {
f.push_str("null");
Ok(())
}
Primitive::StorageKey(_, _) => panic!("Unexpected StorageKey"),
Primitive::RawString(_) => panic!("Unexpected RawString"),
Primitive::GraphQLModuleDependency(_) => panic!("Unexpected GraphQLModuleDependency"),
Primitive::JSModuleDependency { .. } => panic!("Unexpected JSModuleDependency"),
Primitive::ResolverModuleReference { .. } => panic!("Unexpected ResolverModuleReference"),
Primitive::PropertyAccessor(_) => panic!("Unexpected PropertyAccessor"),
Primitive::DynamicImport { .. } => panic!("Unexpected DynamicImport"),
Primitive::RelayResolverModel { .. } => panic!("Unexpected RelayResolver"),
}
}
View on GitHub (pinned to 668b1b85e0)
Solutions
- Move the @module usage to the selection position (an inline fragment with @module spreading the module fragment) instead of an argument
- Ensure arguments only contain literal values; module references must go through the module import path (write_module_import) not write_constant_value
- Audit custom transforms that place GraphQLModuleDependency primitives in argument values and remove them
- Update Relay compiler if the IR no longer matches what the printer expects
Example fix
// before
... on Post @module(as: "User_post") { arg: someModule }
// after
... on Post @module(as: "User_post") { someField } // module spreads as selections, not arguments Defensive patterns
Strategy: validation
Validate before calling
function assertNoModuleDepsInArgs(doc) {
visit(doc, {
Argument(node) {
if (referencesModuleDirective(node.value)) {
throw new Error(`Module dependencies cannot be used in argument '${node.name.value}'`);
}
}
});
} Type guard
const isModuleRef = (v) => typeof v === 'string' && v.includes('@module'); Try / catch
try {
generateArtifacts();
} catch (e) {
if (String(e).includes('Unexpected GraphQLModuleDependency')) {
// move the @module usage from arguments into selections
}
throw e;
} Prevention
- Use @module only on inline fragments (selections), never in arguments
- Keep argument values strictly literal
- Audit custom transforms for module dependencies in value slots
- Update the compiler as a unit with runtime packages
When it happens
Trigger: A @module/@match-produced GraphQLModuleDependency primitive flowing into an argument value or constant position — e.g. using a module fragment spread/dependency as (or inside) a printed argument rather than as a selection.
Common situations: Misusing @module/@match so the module dependency lands in an argument instead of a selection; custom transforms building module dependencies inside argument values; upgrading the compiler and old IR shapes now reaching write_constant_value.
Related errors
- Unexpected StorageKey
- Unexpected RawString
- Unexpected JSModuleDependency
- Unexpected ResolverModuleReference
- Unexpected PropertyAccessor
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/5f24fa0531f8dfb7.
Report an issue: GitHub.