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

  1. Change the @defer usage to omit `if` entirely when it should always defer, or use `@defer(if: $someVariable)` with a Boolean variable.
  2. Ensure skip_unreachable_node / constant-condition transforms run before codegen; update the relay compiler to a version matching your transform configuration.
  3. Add a Relay compiler validation rule (or lint) that rejects @defer `if` arguments that are not variables or `true`.
  4. 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

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


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