{"record":{"id":"6f21d226f21a2acb","repo":"facebook/relay","slug":"unexpected-value-for-defer-if-argument-other","errorCode":null,"errorMessage":"unexpected value for @defer if argument: {other:?}","messagePattern":"unexpected value for @defer if argument: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-codegen/src/build_ast.rs","lineNumber":1966,"sourceCode":"    }\n\n    fn build_defer_normalization(\n        &mut self,\n        context: &mut ContextualMetadata,\n        inline_fragment: &InlineFragment,\n        defer: &Directive,\n    ) -> Primitive {\n        let next_selections = self.build_selections(context, inline_fragment.selections.iter());\n        let DeferDirective { if_arg, label_arg } = DeferDirective::from(\n            defer,\n            &self.project_config.schema_config.defer_stream_interface,\n        );\n        let if_variable_name = if_arg.and_then(|arg| match &arg.value.item {\n            // `true` is the default, remove as the AST is typed just as a variable name string\n            // `false` constant values should've been transformed away in skip_unreachable_node\n            Value::Constant(ConstantValue::Boolean(true)) => None,\n            Value::Variable(var) => Some(var.name.item),\n            other => panic!(\"unexpected value for @defer if argument: {other:?}\"),\n        });\n        let label_name = label_arg.unwrap().value.item.expect_string_literal();\n\n        Primitive::Key(self.object(object! {\n            if_: Primitive::string_or_null(if_variable_name.map(|variable_name| variable_name.0)),\n            kind: Primitive::String(CODEGEN_CONSTANTS.defer),\n            label: Primitive::String(label_name),\n            selections: next_selections,\n        }))\n    }\n\n    fn build_stream(\n        &mut self,\n        context: &mut ContextualMetadata,\n        linked_field: &LinkedField,\n        stream: &Directive,\n    ) -> Primitive {\n        let next_selections = self.build_linked_field_and_handles(","sourceCodeStart":1948,"sourceCodeEnd":1984,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-codegen/src/build_ast.rs#L1948-L1984","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfragment F on User @defer(if: false) { name }\n\n// after\nfragment F on User @defer { name }","handlingStrategy":"validation","validationCode":"// GraphQL/JS validation before handing to relay compiler\nfunction validateDeferIf(doc) {\n  visit(doc, {\n    Directive(node) {\n      if (node.name.value === 'defer') {\n        const ifArg = node.arguments.find(a => a.name.value === 'if');\n        if (ifArg && ifArg.value.kind !== 'Variable' &&\n            !(ifArg.value.kind === 'BooleanValue' && ifArg.value.value === true)) {\n          throw new Error('@defer if must be a variable or omitted, got: ' + JSON.stringify(ifArg.value));\n        }\n      }\n    }\n  });\n}","typeGuard":"function isValidDeferIf(value) {\n  return value == null ||\n    (value.kind === 'Variable') ||\n    (value.kind === 'BooleanValue' && value.value === true);\n}","tryCatchPattern":"try {\n  compile(relayConfig);\n} catch (e) {\n  if (String(e).includes('unexpected value for @defer if argument')) {\n    console.error('Rewrite @defer(if: ...) to a variable or omit it:', e.message);\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["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"],"tags":["relay-compiler","graphql","defer-directive","panic"],"backgroundTag":"invalid-directive-argument","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}