{"record":{"id":"ec819c1dc49aaddd","repo":"zeroclaw-labs/zeroclaw","slug":"estimated-cost-must-be-a-finite-non-negative-valu","errorCode":null,"errorMessage":"Estimated cost must be a finite, non-negative value","messagePattern":"Estimated cost must be a finite, non-negative value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/cost/tracker.rs","lineNumber":109,"sourceCode":"        self.lock_storage().path.clone()\n    }\n\n    /// Check if a request is within budget.\n    pub fn check_budget(&self, estimated_cost_usd: f64) -> Result<BudgetCheck> {\n        let config = self.config_snapshot();\n        if !config.enabled {\n            return Ok(BudgetCheck::Allowed);\n        }\n\n        if !estimated_cost_usd.is_finite() || estimated_cost_usd < 0.0 {\n            ::zeroclaw_log::record!(\n                WARN,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                    .with_attrs(::serde_json::json!({\"estimated_cost_usd\": estimated_cost_usd})),\n                \"cost budget check rejected: estimated cost is not finite or is negative\"\n            );\n            anyhow::bail!(\"Estimated cost must be a finite, non-negative value\");\n        }\n\n        let mut storage = self.lock_storage();\n        let (daily_cost, monthly_cost) = storage.get_aggregated_costs()?;\n\n        // Check daily limit\n        let projected_daily = daily_cost + estimated_cost_usd;\n        if projected_daily > config.daily_limit_usd {\n            return Ok(BudgetCheck::Exceeded {\n                current_usd: daily_cost,\n                limit_usd: config.daily_limit_usd,\n                period: UsagePeriod::Day,\n            });\n        }\n\n        // Check monthly limit\n        let projected_monthly = monthly_cost + estimated_cost_usd;\n        if projected_monthly > config.monthly_limit_usd {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/cost/tracker.rs#L91-L127","documentation":"CostTracker::check_budget validates the per-call cost estimate before comparing it against daily and monthly spending limits, rejecting NaN, infinity, and negative values. The guard exists because one non-finite float would poison every subsequent comparison and render all budget limits meaningless. The rejection is logged as a WARN Reject event carrying the offending estimated_cost_usd attribute.","triggerScenarios":"Calling `check_budget(estimated_cost_usd)` where the estimate came from math on a missing or unparsed model price (NaN propagates through arithmetic), or where a subtraction bug produced a negative number. Tests invalid_budget_estimate_is_rejected and check_tool_loop_budget pin this behavior.","commonSituations":"The provider pricing table has no entry for the active model so cost math yields NaN; a price string fails to parse and the error path leaks into the estimate; tool-loop estimates go negative after refunds or discount subtractions.","solutions":["Trace the inputs: log the model price and token counts that produced the estimate before calling check_budget","Default missing/unparseable prices to 0.0 instead of letting NaN propagate","Guard the call site: skip or zero the estimate when `!c.is_finite() || c < 0.0`","Add a unit test with the exact pricing data that produced the bad value to lock the fix in"],"exampleFix":"// before\nlet check = tracker.check_budget(estimated)?; // panics path on NaN\n\n// after\nlet estimated = if estimated.is_finite() && estimated >= 0.0 { estimated } else { 0.0 };\nlet check = tracker.check_budget(estimated)?;","handlingStrategy":"validation","validationCode":"fn sanitize_cost(c: f64) -> f64 {\n    if c.is_finite() && c >= 0.0 { c } else { 0.0 }\n}\n\nlet estimate = sanitize_cost(estimate);\nlet check = tracker.check_budget(estimate)?;","typeGuard":"fn is_valid_cost(c: f64) -> bool {\n    c.is_finite() && c >= 0.0\n}","tryCatchPattern":"match tracker.check_budget(estimate) {\n    Ok(check) => Ok(check),\n    Err(e) if !estimate.is_finite() || estimate < 0.0 => {\n        tracing::error!(estimate, \"bad cost estimate; treating as 0\");\n        tracker.check_budget(0.0)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Never unwrap Option<f64> prices into arithmetic — default to 0.0 and log the missing model","Unit-test cost math with missing-price and zero-token inputs","Assert is_valid_cost on every value crossing into CostTracker"],"tags":["cost-tracking","budget","nan","floating-point","validation"],"backgroundTag":"nan-float-value-rejected","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}