{"record":{"id":"48988b8e3db7cc91","repo":"hasura/graphql-engine","slug":"expected-array-or-null-for-right-hand-value-of-con","errorCode":null,"errorMessage":"Expected array or null for right-hand value of contains operation","messagePattern":"Expected array or null for right-hand value of contains operation","errorType":"error_code","errorClass":"ConditionError","httpStatus":null,"severity":"error","filePath":"v3/crates/auth/authorization-rules/src/condition.rs","lineNumber":21,"sourceCode":"use std::fmt::Display;\n\nuse hasura_authn_core::{SessionVariableName, SessionVariables};\n\nuse crate::ConditionCache;\nuse metadata_resolve::{\n    BinaryOperation, Condition, ConditionHash, Conditions, UnaryOperation, ValueExpression,\n};\nuse open_dds::query::ArgumentName;\n\n#[derive(Debug, PartialEq, Eq, thiserror::Error)]\npub enum ConditionError {\n    #[error(\"Session variable not found: {name}\")]\n    SessionVariableNotFound { name: SessionVariableName },\n    #[error(\"Serde error: {error}\")]\n    SerdeError { error: String },\n    #[error(\"Condition {condition_hash} not found\")]\n    ConditionNotFound { condition_hash: ConditionHash },\n    #[error(\"Expected array or null for right-hand value of contains operation\")]\n    ExpectedArrayOrNullForContains,\n    #[error(\"Expected number for {side}-hand value of comparison operation\")]\n    ExpectedNumberForComparison { side: Side },\n    #[error(\n        \"Number for {side}-hand value of comparison operation is outside precision or range of a double-precision float\"\n    )]\n    NumberOutOfRange { side: Side },\n    #[error(\n        \"Tried to combine a predicate with a literal in argument presets for argument {argument_name}\"\n    )]\n    CouldNotCombinePredicateAndLiteralArgumentPresets { argument_name: ArgumentName },\n}\n\n// evaluate conditions used in permissions\nfn evaluate_condition(\n    condition: &Condition,\n    session_variables: &SessionVariables,\n) -> Result<bool, ConditionError> {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/auth/authorization-rules/src/condition.rs#L3-L39","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nfilter: { column: tags, operator: contains, value: \"admin\" }\n# after\nfilter: { column: tags, operator: contains, value: [\"admin\"] }","handlingStrategy":"type-guard","validationCode":"const rhv = resolveOperand(rule.contains);\nif (rhv !== null && !Array.isArray(rhv)) throw new Error('contains RHS must be an array or null');","typeGuard":"const isArrayOrNull = (v: unknown): v is unknown[] | null => v === null || Array.isArray(v);","tryCatchPattern":"null","preventionTips":["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"],"tags":["auth","authorization","contains","type-mismatch","rust","hasura"],"backgroundTag":"authorization-type-mismatch","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}