zeroclaw-labs/zeroclaw · error
providers.models.{profile_name}.pricing.{key}: value must no
Error message
providers.models.{profile_name}.pricing.{key}: value must not be NaN What it means
Every value in a model profile's `pricing` map is checked with f64::is_nan(). TOML permits bare `nan` float literals, so a NaN can enter config directly, and NaN would silently poison all downstream cost arithmetic (every comparison against NaN is false). Validation rejects it up front, naming the offending pricing key.
Source
Thrown at crates/zeroclaw-config/src/schema.rs:21632
validate_temperature(temp).map_err(|e| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({
"profile": profile_name,
"temperature": temp,
"error": format!("{}", e),
})),
"providers.models.<alias>.temperature rejected"
);
anyhow::Error::msg(format!("providers.models.{profile_name}.temperature: {e}"))
})?;
}
for (key, value) in &profile.pricing {
if value.is_nan() {
anyhow::bail!(
"providers.models.{profile_name}.pricing.{key}: value must not be NaN"
);
}
if *value < 0.0 {
anyhow::bail!(
"providers.models.{profile_name}.pricing.{key}: value must be >= 0.0 (got {value})"
);
}
}
}
// Non-fatal validation warnings: surfaced both via tracing (CLI sees
// on stderr) and via Config::collect_warnings (gateway HTTP returns
// structured to dashboard callers). Single source of truth lives in
// collect_warnings; emit each one to tracing here so the existing
// log behavior is preserved.
for w in self.collect_warnings() {
::zeroclaw_log::record!(View on GitHub (pinned to 88bb9c8533)
Solutions
- Replace nan with the real numeric price for the named key
- If the price is genuinely unknown, use 0.0 so cost estimates read zero rather than corrupting arithmetic — or omit the key
- Guard computed prices before writing config: skip or clamp values that fail is_finite()
Example fix
# before [providers.models.gpt-x.pricing] input = nan output = 0.002 # after [providers.models.gpt-x.pricing] input = 0.001 output = 0.002
Defensive patterns
Strategy: validation
Validate before calling
fn pricing_precheck(cfg: &zeroclaw_config::Config) -> Result<(), String> {
for (name, profile) in &cfg.providers.models.entries {
for (key, value) in &profile.pricing {
if value.is_nan() {
return Err(format!("providers.models.{name}.pricing.{key} is NaN"));
}
}
}
Ok(())
} Type guard
fn pricing_is_finite(pricing: &std::collections::HashMap<String, f64>) -> bool {
pricing.values().all(|v| v.is_finite())
} Try / catch
if let Err(err) = config.validate() {
if err.to_string().contains("must not be NaN") {
// replace the named pricing key with a real number (0.0 if unknown) and reload
}
} Prevention
- Never write `nan` literals in TOML pricing tables; use 0.0 for unknown prices
- Run is_finite() guards in any generator that computes prices
- Round-trip generated configs through TOML serialization that rejects NaN
When it happens
Trigger: Write `pricing = { input = nan }` (TOML nan literal) on any [providers.models.*] profile, or generate config programmatically from a computation that yields 0.0/0.0 or f64::NAN.
Common situations: Placeholder values for token prices the operator doesn't know yet; per-token math derived by dividing quoted prices; tooling that serializes floats without a finite-guard (serde_json rejects NaN, TOML does not — TOML configs are the leak path).
Related errors
- providers.models.{profile_name}.pricing.{key}: value must be
- providers.models.{profile_name}.uri must use http/https
- providers.models.ollama.{alias}.model uses ':cloud', but uri
- providers.models.ollama.{alias}.model uses ':cloud', but no
- cloud_ops.iac_tools must not be empty when cloud_ops is enab
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/08d868260ae1df17.
Report an issue: GitHub.