facebook/relay · error
Unexpected ResolverModuleReference
Error message
Unexpected ResolverModuleReference
What it means
Primitive::ResolverModuleReference points to a Relay Resolver module and is printed through write_resolver_module_reference (requiring a named import). If it appears in write_constant_value — i.e. in an ordinary argument/literal position — the printer cannot serialize it and panics, preserving the invariant that resolver references are only printed via the resolver path.
Source
Thrown at compiler/crates/relay-codegen/src/printer.rs:934
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
- Reference the resolver only as a field (via @relay_resolver / resolver field definitions), not inside argument values
- Move the resolver usage out of arguments/lists printed as constants and into a resolver field position
- Fix custom transforms so ResolverModuleReference primitives are only created for resolver field printing
- Update the Relay compiler if transforms and printer are out of sync
Example fix
// before
user { name(arg: <ResolverModuleReference>) }
// after
user { name @relay_resolver(module: "./myResolver") } Defensive patterns
Strategy: validation
Validate before calling
// Ensure resolvers are only referenced as fields, never in arguments:
function assertResolverUsage(doc) {
visit(doc, {
Argument(node) {
if (referencesResolver(node)) {
throw new Error(`Resolver reference not allowed in argument '${node.name.value}'`);
}
}
});
} Type guard
const isResolverRef = (p) => p != null && p.type === 'resolverModuleReference';
Try / catch
try {
generateArtifacts();
} catch (e) {
if (String(e).includes('Unexpected ResolverModuleReference')) {
// move the resolver usage to a resolver field position
}
throw e;
} Prevention
- Use @relay_resolver / resolver field definitions only on fields
- Never place resolver references inside argument values
- Keep Relay compiler and runtime versions aligned
- Test Relay Resolver documents through codegen in CI
When it happens
Trigger: A resolver field reference primitive flowing into write_constant_value — e.g. a Relay Resolver value used inside an argument, list, or object literal instead of as a field; custom transforms placing ResolverModuleReference in constant positions.
Common situations: Misconfigured Relay Resolvers where a resolver is referenced from an argument (@arguments/@static_arg) rather than a field; custom IR passes emitting resolver references in wrong positions; compiler version mismatch between transform output and printer expectations.
Related errors
- Expected a named import for Relay Resolvers
- Unexpected StorageKey
- Unexpected RawString
- Unexpected GraphQLModuleDependency
- Unexpected JSModuleDependency
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/07a3f3cc701ccb3e.
Report an issue: GitHub.