hasura/graphql-engine · error · ConditionError
Expected number for {side}-hand value of comparison operatio
Error message
Expected number for {side}-hand value of comparison operation What it means
For comparison operations in authorization rules, at least one side must resolve to a number when required by the operator (e.g. gt/lt on numeric columns). If the operand on the indicated side (left or right) is a non-numeric value — a string session variable, null, or a wrongly typed literal — ConditionError::ExpectedNumberForComparison is thrown, identifying which side failed.
Source
Thrown at v3/crates/auth/authorization-rules/src/condition.rs:23
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> {
match condition {
Condition::All(conditions) => conditions.iter().try_fold(true, |acc, condition| {View on GitHub (pinned to 724551b9ae)
Solutions
- Check the reported `{side}` and make that operand a number: fix the JWT claim to be a JSON number, or the metadata literal to be unquoted
- Align the rule's operand types with the column type in the data source schema
- Re-apply metadata after schema type changes
Example fix
# before
{ operator: gt, column: age, value: { session: x_hasura_min_age } } # claim = "18"
# after: emit claim as JSON number
"x-hasura-min-age": 18 Defensive patterns
Strategy: type-guard
Validate before calling
const v = resolveOperand(rule.value);
if (rule.operator === 'gt' && typeof v !== 'number') throw new Error(`${rule.side} operand must be a number`); Type guard
const isNumber = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);
Try / catch
null
Prevention
- Serialize numeric claims as JSON numbers, not strings
- Match operand types to column types in rules
- Re-validate rules after schema type changes
When it happens
Trigger: A rule comparing a numeric column against a session variable whose claim is a string (`\"42\"`), or comparing a string column with a numeric literal; session variable missing-but-defaulted to a non-numeric value.
Common situations: JWT numeric claims serialized as strings by the auth server; metadata literals written as strings; schema type changes (column became numeric) without updating the rule.
Related errors
- Expected array or null for right-hand value of contains oper
- 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/b0879ac2e0d5ea28.
Report an issue: GitHub.