hasura/graphql-engine · error · ConditionError
Expected array or null for right-hand value of contains oper
Error message
Expected array or null for right-hand value of contains operation
What it means
When evaluating a `contains` operation in an authorization rule, the right-hand operand must be an array or null. If the metadata or session variables supply any other JSON type (string, number, object), ConditionError::ExpectedArrayOrNullForContains is raised and the permission check fails.
Source
Thrown at v3/crates/auth/authorization-rules/src/condition.rs:21
use std::fmt::Display;
use hasura_authn_core::{SessionVariableName, SessionVariables};
use crate::ConditionCache;
use metadata_resolve::{
BinaryOperation, Condition, ConditionHash, Conditions, UnaryOperation, ValueExpression,
};
use open_dds::query::ArgumentName;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum ConditionError {
#[error("Session variable not found: {name}")]
SessionVariableNotFound { name: SessionVariableName },
#[error("Serde error: {error}")]
SerdeError { error: String },
#[error("Condition {condition_hash} not found")]
ConditionNotFound { condition_hash: ConditionHash },
#[error("Expected array or null for right-hand value of contains operation")]
ExpectedArrayOrNullForContains,
#[error("Expected number for {side}-hand value of comparison operation")]
ExpectedNumberForComparison { side: Side },
#[error(
"Number for {side}-hand value of comparison operation is outside precision or range of a double-precision float"
)]
NumberOutOfRange { side: Side },
#[error(
"Tried to combine a predicate with a literal in argument presets for argument {argument_name}"
)]
CouldNotCombinePredicateAndLiteralArgumentPresets { argument_name: ArgumentName },
}
// evaluate conditions used in permissions
fn evaluate_condition(
condition: &Condition,
session_variables: &SessionVariables,
) -> Result<bool, ConditionError> {View on GitHub (pinned to 724551b9ae)
Solutions
- Make the right-hand value a JSON array in metadata (e.g. `["admin"]` not `"admin"`)
- Ensure session variables used with contains are arrays in the JWT claims/webhook response
- If null is a valid 'no check' case, allow null; otherwise fix the producer of the value
Example fix
# before
filter: { column: tags, operator: contains, value: "admin" }
# after
filter: { column: tags, operator: contains, value: ["admin"] } Defensive patterns
Strategy: type-guard
Validate before calling
const rhv = resolveOperand(rule.contains);
if (rhv !== null && !Array.isArray(rhv)) throw new Error('contains RHS must be an array or null'); Type guard
const isArrayOrNull = (v: unknown): v is unknown[] | null => v === null || Array.isArray(v);
Try / catch
null
Prevention
- Emit list claims as JSON arrays in the auth server
- Use array literals in metadata for contains rules
- Test permission rules with real tokens in CI
When it happens
Trigger: A rule like `contains: { session: x_hasura_roles }` where the session variable resolves to a plain string instead of a JSON array; or metadata writing `contains` with a scalar literal on the right-hand side.
Common situations: Auth server emitting roles as a comma-separated string instead of an array; JWT claim shape changes; rules authored assuming set semantics with scalar values.
Related errors
- Expected number for {side}-hand value of comparison operatio
- Condition {condition_hash} not found
- Session variable not found: {name}
- Serde error: {error}
- the type of the comparable field '{field_name}' ({field_type
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/48988b8e3db7cc91.
Report an issue: GitHub.