facebook/relay · error

Expected filters_arg to have been previously validated.

Error message

Expected filters_arg to have been previously validated.

What it means

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.

Source

Thrown at compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs:235

    // validated first as part of validate_connections validation step.
    let key = match key_arg {
        Some((_, value)) => value
            .get_string_literal()
            .expect("Expected key_arg to have been previously validated."),
        None => "".intern(),
    };
    let handle= match handler_arg {
         Some((_, value)) => value.get_string_literal().expect("Expected handler_arg to have been previously validated."),
         None => default_handler.expect("Expected handler_arg to have been previously validated or a default to have been provided."),
     };
    let filters = match filters_arg {
        Some((_, value)) => match value {
            ConstantValue::List(list_val) => Some(
                list_val
                    .iter()
                    .map(|val| {
                        val.get_string_literal()
                            .expect("Expected filters_arg to have been previously validated.")
                    })
                    .collect::<Vec<_>>(),
            ),
            ConstantValue::Null() => None,
            _ => unreachable!("Expected filters_arg to have been previously validated.",),
        },
        None => default_filters,
    };
    let dynamic_key = match dynamic_key_arg {
        Some((_, value)) => match value {
            Value::Variable(_) => Some(value.clone()),
            _ => unreachable!("Expected dynamic_key_arg to have been previously validated."),
        },
        None => None,
    };
    let handle_args = handle_args_arg.map(|arg| {
        if let Value::Object(args) = &arg.value.item {
            args.clone()

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure every value inside the filters argument of @connection/@handle directives is a quoted string literal, e.g. filters: ["isAdmin"].
  2. Find the custom transform or code path that constructed the directive and route it through the standard validation pass before the handle field transform.
  3. If generating IR programmatically, emit only Value::Constant(ConstantValue::String(..)) for filter items.
  4. Align all relay compiler crates/dependencies to the same version so validation and transforms agree.
  5. If the document looks valid, minimize it and file a relay-compiler issue — this is meant to be unreachable.

Example fix

// before
fragment F on Query {
  users(first: 10) @connection(key: "F_users", filters: [2]) { ... }
}
// after
fragment F on Query {
  users(first: 10) @connection(key: "F_users", filters: ["isAdmin"]) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

function validateFiltersArg(directive) {
  const filters = directive.arguments?.find(a => a.name === 'filters');
  if (!filters) return true; // filters is optional
  if (filters.value.kind !== 'ListValue') return false;
  return filters.value.values.every(
    v => v.kind === 'StringValue' || v.kind === 'NullValue'
  );
}
// before compiling:
// if (!validateFiltersArg(directive)) throw new Error('filters must be string literals');

Type guard

function isStringLiteralList(v) {
  return v != null &&
    v.kind === 'ListValue' &&
    v.values.every(x => x.kind === 'StringValue');
}

Try / catch

try {
  const values = extractValuesFromHandleFieldDirective(directive);
} catch (e) {
  if (String(e.message).includes('filters_arg')) {
    reportCompilerBug(directive.loc, 'filters argument contains non-string literals');
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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