{"record":{"id":"8557d8695a5ebfde","repo":"hasura/graphql-engine","slug":"number-for-side-hand-value-of-comparison-operati","errorCode":null,"errorMessage":"Number for {side}-hand value of comparison operation is outside precision or range of a double-precision float","messagePattern":"Number for (.+?)-hand value of comparison operation is outside precision or range of a double-precision float","errorType":"error_code","errorClass":"ConditionError","httpStatus":null,"severity":"error","filePath":"v3/crates/auth/authorization-rules/src/condition.rs","lineNumber":25,"sourceCode":"use 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> {\n    match condition {\n        Condition::All(conditions) => conditions.iter().try_fold(true, |acc, condition| {\n            Ok(acc && evaluate_condition(condition, session_variables)?)\n        }),","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/auth/authorization-rules/src/condition.rs#L7-L43","documentation":"Thrown by the authorization-rules condition evaluator when one side of a comparison operation (e.g. _eq, _gt, _lte on numbers) holds a number that cannot be represented exactly as an f64 double-precision float. The engine compares numeric values as doubles, so values requiring more precision (e.g. huge integers or high-precision decimals) are rejected rather than silently rounded.","triggerScenarios":"Defining a permission rule with a comparison operator where the left-hand (session variable/column) or right-hand (literal) value is an integer exceeding 2^53 or a decimal with more precision than an f64 can hold, and the value then fails to round-trip through f64.","commonSituations":"Using 64-bit or UUID-like numeric IDs, snowflake IDs, or high-precision decimal literals in permission comparison expressions in OpenDD auth configuration.","solutions":["Reduce the compared value's magnitude/precision so it fits exactly in a double (integers within ±2^53)","Store and compare the value as a string instead of a number if exactness matters","Move the comparison out of the permission rule into application logic or a computed expression that supports arbitrary precision"],"exampleFix":"// before\n{ \"type\": \"_eq\", \"left\": { \"column\": \"snowflake_id\" }, \"right\": 9007199254740993 }\n// after\n{ \"type\": \"_eq\", \"left\": { \"column\": \"snowflake_id_str\" }, \"right\": \"9007199254740993\" }","handlingStrategy":"validation","validationCode":"fn fits_f64(n: &serde_json::Number) -> bool {\n    if let Some(i) = n.as_i64() {\n        i.abs() <= (2i64.pow(53))\n    } else {\n        n.as_f64().map(|f| serde_json::Number::from_f64(f).as_ref() == Some(n)).unwrap_or(false)\n    }\n}\nassert!(fits_f64(&right_hand_value));","typeGuard":"fn isDoubleSafe(n: serde_json::Number) -> bool {\n    n.as_f64()\n        .and_then(|f| serde_json::Number::from_f64(f))\n        .map(|rt| rt == n)\n        .unwrap_or(false)\n}","tryCatchPattern":"match condition_eval {\n    Err(ConditionError::NumberOutOfRange { side }) => return_policy_deny_with_reason(side),\n    r => r,\n}","preventionTips":["Keep numeric literals in permission rules within ±2^53","Prefer string-typed columns for very large identifiers","Validate preset values with a round-trip f64 check in config CI"],"tags":["auth","authorization","numeric-precision","permission-rules"],"backgroundTag":"numeric-precision-loss","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}