facebook/relay · error
Unexpected custom directives: {:#?}
Error message
Unexpected custom directives: {:#?} What it means
When building an inline fragment in codegen, the compiler only supports inline fragments with no directives, @client, @defer/@stream-style metadata, @match, @module, or @catch. If an inline fragment carries other custom directives after client directives were handled, the compiler does not know how to represent them and panics, printing the full directive list.
Source
Thrown at compiler/crates/relay-codegen/src/build_ast.rs:2367
selections: selections,
type_: Primitive::SkippableNull,
abstract_key: Primitive::SkippableNull,
}));
let aliased_fragment = Primitive::Key(self.object(object! {
fragment: primitive,
kind: Primitive::String(CODEGEN_CONSTANTS.aliased_inline_fragment_spread),
name: Primitive::String(fragment_alias_metadata.alias.item),
}));
if let Some(catch_metadata) =
CatchMetadataDirective::find(&inline_frag.directives)
{
self.build_catch_node(catch_metadata, aliased_fragment)
} else {
aliased_fragment
}
} else {
// TODO(T63559346): Handle anonymous inline fragments with no directives
panic!(
"Unexpected custom directives: {:#?}",
inline_frag.directives
);
}
}
Some(type_condition) => {
if self.variant == CodegenVariant::Normalization {
let is_abstract_inline_fragment = type_condition.is_abstract_type();
if is_abstract_inline_fragment {
// Maintain a few invariants:
// - InlineFragment (and `selections` arrays generally) cannot be empty
// - Don't emit a TypeDiscriminator under an InlineFragment unless it has
// a different abstractKey
// This means we have to handle two cases:
// - The inline fragment only contains a TypeDiscriminator with the same
// abstractKey: replace the Fragment w the Discriminator
// - The inline fragment contains other selections: return all the selections
// minus any Discriminators w the same keyView on GitHub (pinned to 668b1b85e0)
Solutions
- Remove unsupported custom directives from the inline fragment or move the directive to a supported position (e.g. the parent field or a named fragment).
- Give the inline fragment a type condition so Relay can emit it (TODO T63559346: anonymous inline fragments without directives are unsupported here).
- Implement @include/@skip semantics via variables handled by transforms, or restructure the query so those directives apply to fields/spreads Relay supports.
- If the directive is your own, strip it with a custom compiler transform before codegen.
Example fix
// before
... on User @myDirective { name }
// after
... on User { name } Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED = new Set(['client', 'defer', 'stream', 'match', 'module', 'catch', 'required', 'relay']);
function validateInlineFragmentDirectives(fragment) {
for (const d of fragment.directives || []) {
if (!SUPPORTED.has(d.name.value)) {
throw new Error('Unsupported directive @' + d.name.value + ' on inline fragment');
}
}
if (!fragment.typeCondition && !(fragment.directives || []).length) {
console.warn('Anonymous inline fragment without directives is unsupported by relay codegen');
}
} Type guard
function hasOnlySupportedDirectives(fragment) {
const allowed = new Set(['client', 'defer', 'stream', 'match', 'module', 'catch', 'required', 'relay']);
return (fragment.directives || []).every(d => allowed.has(d.name.value));
} Try / catch
try {
relayCompiler.compile();
} catch (e) {
if (String(e).includes('Unexpected custom directives')) {
console.error('Remove unsupported directives from inline fragments:', e.message);
} else throw e;
} Prevention
- Do not put custom/server directives on inline fragments
- Always give inline fragments a type condition
- Strip custom directives with a compiler transform before codegen
- Document allowed directives for your GraphQL authors
When it happens
Trigger: Compiling a document with an anonymous inline fragment carrying custom (non-Relay) directives, e.g. `... on Type @myDirective` or `... @include(if: $x)` in a position Relay's codegen does not support.
Common situations: Using server-only directives like @include/@skip on inline fragments in reader codegen paths; custom schema directives left on inline fragments; anonymous inline fragments (`... ` with no type condition) that Relay cannot type.
Related errors
- Expect the module import inline fragment to have a type
- unexpected value for @defer if argument: {other:?}
- unexpected value for @stream if argument: {other:?}
- Expected a scalar field.
- Expected filters_arg to have been previously validated.
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/5807e0750529cc2f.
Report an issue: GitHub.