{"record":{"id":"beb5cefde67f6b02","repo":"facebook/relay","slug":"expected-filters-arg-to-have-been-previously-valid","errorCode":null,"errorMessage":"Expected filters_arg to have been previously validated.","messagePattern":"Expected filters_arg to have been previously validated\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs","lineNumber":235,"sourceCode":"    // validated first as part of validate_connections validation step.\n    let key = match key_arg {\n        Some((_, value)) => value\n            .get_string_literal()\n            .expect(\"Expected key_arg to have been previously validated.\"),\n        None => \"\".intern(),\n    };\n    let handle= match handler_arg {\n         Some((_, value)) => value.get_string_literal().expect(\"Expected handler_arg to have been previously validated.\"),\n         None => default_handler.expect(\"Expected handler_arg to have been previously validated or a default to have been provided.\"),\n     };\n    let filters = match filters_arg {\n        Some((_, value)) => match value {\n            ConstantValue::List(list_val) => Some(\n                list_val\n                    .iter()\n                    .map(|val| {\n                        val.get_string_literal()\n                            .expect(\"Expected filters_arg to have been previously validated.\")\n                    })\n                    .collect::<Vec<_>>(),\n            ),\n            ConstantValue::Null() => None,\n            _ => unreachable!(\"Expected filters_arg to have been previously validated.\",),\n        },\n        None => default_filters,\n    };\n    let dynamic_key = match dynamic_key_arg {\n        Some((_, value)) => match value {\n            Value::Variable(_) => Some(value.clone()),\n            _ => unreachable!(\"Expected dynamic_key_arg to have been previously validated.\"),\n        },\n        None => None,\n    };\n    let handle_args = handle_args_arg.map(|arg| {\n        if let Value::Object(args) = &arg.value.item {\n            args.clone()","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs#L217-L253","documentation":"This is an internal invariant panic in Relay's HandleFieldTransform. The filters argument of a @connection/@handle directive must be a list of string literals; an earlier validation pass guarantees this, so when extract_values_from_handle_field_directive_helper encounters a list element that is not a String literal (or the whole value is neither a List nor Null), it panics with this message instead of emitting a friendly diagnostic.","triggerScenarios":"Calling extract_values_from_handle_field_directive (via build_handle_field_directive_from_connection_directive) on a @connection(handle:...) or @handle directive whose filters list contains non-string-literal items — e.g. an Int, Boolean, Enum literal, or a nested list — meaning the earlier validation did not run or was bypassed.","commonSituations":"Hand-writing or programmatically generating IR that skips the validation visitor; custom transforms that build handle directives directly; a Relay compiler version mismatch where a new constant value kind reaches the util unvalidated; reusing stale IR documents across compiler versions.","solutions":["Ensure every value inside the filters argument of @connection/@handle directives is a quoted string literal, e.g. filters: [\"isAdmin\"].","Find the custom transform or code path that constructed the directive and route it through the standard validation pass before the handle field transform.","If generating IR programmatically, emit only Value::Constant(ConstantValue::String(..)) for filter items.","Align all relay compiler crates/dependencies to the same version so validation and transforms agree.","If the document looks valid, minimize it and file a relay-compiler issue — this is meant to be unreachable."],"exampleFix":"// before\nfragment F on Query {\n  users(first: 10) @connection(key: \"F_users\", filters: [2]) { ... }\n}\n// after\nfragment F on Query {\n  users(first: 10) @connection(key: \"F_users\", filters: [\"isAdmin\"]) { ... }\n}","handlingStrategy":"validation","validationCode":"function validateFiltersArg(directive) {\n  const filters = directive.arguments?.find(a => a.name === 'filters');\n  if (!filters) return true; // filters is optional\n  if (filters.value.kind !== 'ListValue') return false;\n  return filters.value.values.every(\n    v => v.kind === 'StringValue' || v.kind === 'NullValue'\n  );\n}\n// before compiling:\n// if (!validateFiltersArg(directive)) throw new Error('filters must be string literals');","typeGuard":"function isStringLiteralList(v) {\n  return v != null &&\n    v.kind === 'ListValue' &&\n    v.values.every(x => x.kind === 'StringValue');\n}","tryCatchPattern":"try {\n  const values = extractValuesFromHandleFieldDirective(directive);\n} catch (e) {\n  if (String(e.message).includes('filters_arg')) {\n    reportCompilerBug(directive.loc, 'filters argument contains non-string literals');\n  }\n  throw e;\n}","preventionTips":["Only use quoted string literals in filters: [\"...\"] of @connection/@handle directives.","Never construct handle directive IR manually without running the standard validation pass.","Keep all relay-compiler crates at the same version.","Lint GraphQL documents for filters arguments containing numeric or enum values."],"tags":["relay-compiler","graphql","invariant","panic","argument-validation"],"backgroundTag":"unvalidated-graphql-argument","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}