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

  1. Reference the resolver only as a field (via @relay_resolver / resolver field definitions), not inside argument values
  2. Move the resolver usage out of arguments/lists printed as constants and into a resolver field position
  3. Fix custom transforms so ResolverModuleReference primitives are only created for resolver field printing
  4. 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

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


AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02). Data as JSON: /api/errors/07a3f3cc701ccb3e. Report an issue: GitHub.