facebook/relay · error
unexpected value for @defer if argument: {other:?}
Error message
unexpected value for @defer if argument: {other:?} What it means
This panic occurs in the Relay compiler codegen while building the AST for a @defer directive. The compiler expects the `if` argument of @defer to be either the literal `true` (removed since it is the default) or a query variable; after transform passes, anything else (e.g. a constant `false`, string, or literal) is invalid. It signals a mis-typed @defer argument that earlier validation/transforms should have eliminated.
Source
Thrown at compiler/crates/relay-codegen/src/build_ast.rs:1966
}
fn build_defer_normalization(
&mut self,
context: &mut ContextualMetadata,
inline_fragment: &InlineFragment,
defer: &Directive,
) -> Primitive {
let next_selections = self.build_selections(context, inline_fragment.selections.iter());
let DeferDirective { if_arg, label_arg } = DeferDirective::from(
defer,
&self.project_config.schema_config.defer_stream_interface,
);
let if_variable_name = if_arg.and_then(|arg| match &arg.value.item {
// `true` is the default, remove as the AST is typed just as a variable name string
// `false` constant values should've been transformed away in skip_unreachable_node
Value::Constant(ConstantValue::Boolean(true)) => None,
Value::Variable(var) => Some(var.name.item),
other => panic!("unexpected value for @defer if argument: {other:?}"),
});
let label_name = label_arg.unwrap().value.item.expect_string_literal();
Primitive::Key(self.object(object! {
if_: Primitive::string_or_null(if_variable_name.map(|variable_name| variable_name.0)),
kind: Primitive::String(CODEGEN_CONSTANTS.defer),
label: Primitive::String(label_name),
selections: next_selections,
}))
}
fn build_stream(
&mut self,
context: &mut ContextualMetadata,
linked_field: &LinkedField,
stream: &Directive,
) -> Primitive {
let next_selections = self.build_linked_field_and_handles(View on GitHub (pinned to 668b1b85e0)
Solutions
- Change the @defer usage to omit `if` entirely when it should always defer, or use `@defer(if: $someVariable)` with a Boolean variable.
- Ensure skip_unreachable_node / constant-condition transforms run before codegen; update the relay compiler to a version matching your transform configuration.
- Add a Relay compiler validation rule (or lint) that rejects @defer `if` arguments that are not variables or `true`.
- If authoring tools that emit GraphQL, emit `if` only as a variable reference.
Example fix
// before
fragment F on User @defer(if: false) { name }
// after
fragment F on User @defer { name } Defensive patterns
Strategy: validation
Validate before calling
// GraphQL/JS validation before handing to relay compiler
function validateDeferIf(doc) {
visit(doc, {
Directive(node) {
if (node.name.value === 'defer') {
const ifArg = node.arguments.find(a => a.name.value === 'if');
if (ifArg && ifArg.value.kind !== 'Variable' &&
!(ifArg.value.kind === 'BooleanValue' && ifArg.value.value === true)) {
throw new Error('@defer if must be a variable or omitted, got: ' + JSON.stringify(ifArg.value));
}
}
}
});
} Type guard
function isValidDeferIf(value) {
return value == null ||
(value.kind === 'Variable') ||
(value.kind === 'BooleanValue' && value.value === true);
} Try / catch
try {
compile(relayConfig);
} catch (e) {
if (String(e).includes('unexpected value for @defer if argument')) {
console.error('Rewrite @defer(if: ...) to a variable or omit it:', e.message);
process.exitCode = 1;
} else throw e;
} Prevention
- Never write @defer(if: false); omit the directive instead
- Only use variable references for defer/stream if arguments
- Keep relay compiler and transform versions in sync
- Add a CI lint that rejects constant if arguments on @defer
When it happens
Trigger: Compiling a GraphQL document where @defer's `if:` argument is a constant value other than `true` (e.g. `@defer(if: false)` that was not transformed away by skip_unreachable_node), or a non-variable, non-boolean literal such as a string or enum value.
Common situations: Hand-written or third-party-generated GraphQL containing `@defer(if: false)` fed directly to relay compiler instead of going through the normal transform pipeline; custom transforms or an out-of-date compiler version that skipped skip_unreachable_node; templates generating directives programmatically.
Related errors
- unexpected value for @stream if argument: {other:?}
- Unexpected custom directives: {:#?}
- Expected a scalar field.
- Expected filters_arg to have been previously validated.
- expected to find a supported argument as checked before
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/6f21d226f21a2acb.
Report an issue: GitHub.