zeroclaw-labs/zeroclaw · error · anyhow::Error
effective cost limit must be a non-negative finite value
Error message
effective cost limit must be a non-negative finite value
What it means
cost_limit_to_db validates an effective cost limit before it is persisted to the goal extension row: the f64 must be finite and >= 0. NaN, +/-infinity, and negatives are rejected (pinned by update_goal_limits_rejects_invalid_effective_limits). This keeps unparseable or nonsensical values out of the SQLite column where they would silently corrupt later budget enforcement.
Source
Thrown at crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs:232
}
fn token_limit_from_db(value: Option<i64>) -> rusqlite::Result<Option<u64>> {
value
.map(|value| {
u64::try_from(value).map_err(|e| {
rusqlite::Error::FromSqlConversionFailure(
0,
rusqlite::types::Type::Integer,
e.into(),
)
})
})
.transpose()
}
fn cost_limit_to_db(value: f64) -> Result<f64> {
if !value.is_finite() || value < 0.0 {
anyhow::bail!("effective cost limit must be a non-negative finite value");
}
Ok(value)
}
fn cost_limit_from_db(value: Option<f64>) -> rusqlite::Result<Option<f64>> {
match value {
Some(value) if !value.is_finite() || value < 0.0 => {
Err(rusqlite::Error::FromSqlConversionFailure(
0,
rusqlite::types::Type::Real,
format!("invalid effective cost limit {value}").into(),
))
}
other => Ok(other),
}
}
fn goal_limits_to_db(View on GitHub (pinned to 88bb9c8533)
Solutions
- Trace the origin of the f64 and fix the computation (guard divisions, validate parsed config)
- Clamp or reject at your API boundary before calling update_goal_limits
- Encode 'no limit' as None (omitted field), never as infinity
- Add a unit test at your config layer asserting NaN/negative rejection
Example fix
// before
let limit = budget_total / task_count; // 0 tasks => NaN/inf
store.update_goal_limits(id, tokens, Some(limit)).await?;
// after
let limit = budget_total / task_count.max(1.0);
if !limit.is_finite() || limit < 0.0 { anyhow::bail!("invalid cost limit"); }
store.update_goal_limits(id, tokens, Some(limit)).await?; Defensive patterns
Strategy: validation
Validate before calling
let limit = computed_limit();
if !valid_cost_limit(limit) {
anyhow::bail!("cost limit must be a non-negative finite number, got {limit}");
}
store.update_goal_limits(id, tokens, Some(limit)).await?; Type guard
fn valid_cost_limit(v: f64) -> bool {
v.is_finite() && v >= 0.0
} Try / catch
if let Err(ref e) = store.update_goal_limits(id, t, Some(l)).await {
if e.to_string().contains("non-negative finite") {
// the computed limit is NaN/inf/negative: fix the upstream math, not the store
}
} Prevention
- Validate numeric config at load time, not at persistence time
- Represent 'unlimited' as None, never f64::INFINITY
- Guard every division that feeds a limit
- Unit-test config parsers with empty and unset fields
When it happens
Trigger: update_goal_limits called with a computed float such as 0.0/0.0 (NaN), division by zero yielding infinity, or a negative result from subtraction/parse errors; values read from user config without prior validation.
Common situations: Config math producing NaN (unset field defaulting to 0 then divided); 'unlimited' encoded as f64::INFINITY; currency adjustments going negative; JSON null deserialized as 0.0 and then mis-scaled.
Related errors
- Grok CLI model provider received a non-finite temperature va
- KiloCLI model_provider received non-finite temperature value
- Estimated cost must be a finite, non-negative value
- Token usage cost must be a finite, non-negative value
- {flag} = true requires memory.backend = "sqlite" (typed memo
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/88540ff61e0948ff.
Report an issue: GitHub.