Hmbown/CodeWhale · error

Burn rate must be at least $0.01/hr.

Error message

Burn rate must be at least $0.01/hr.

What it means

After rounding to cents, a burn rate of 0 or less is rejected. A sub-cent rate (e.g. 0.005) rounds to $0/hr, which the pace governor would treat as an unbounded rate, silently dropping the requested cap — so the library refuses it instead.

Solutions

  1. Use a burn rate of at least 0.01 $/hr
  2. Round or clamp the value up to $0.01/hr before parsing
  3. If a near-zero cap is intended, disable the burn rate instead of passing a sub-cent value

Example fix

// before
burn_rate: 0.004
// after
burn_rate: 0.01
Defensive patterns

Strategy: validation

Validate before calling

fn burn_rate_ok(v: f64) -> bool { v.is_finite() && ((v * 100.0).round() / 100.0) >= 0.01 }

Try / catch

match parse_burn_rate(input) { Err(e) if e.to_string().contains("$0.01") => eprintln!("rate too small: {}", e), Ok(v) => apply(v), Err(e) => return Err(e), }

Prevention

When it happens

Trigger: Calling parse_burn_amount (via parse_burn_rate or normalize_burn_rate) with a positive but sub-$0.01 amount such as 0.001, or a value that rounds down to 0.

Common situations: Entering an intentionally tiny burn rate in config; unit confusion (entering fractions of a dollar); floating-point values below the cent granularity.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/9d4d08d52851f017. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/operate.rs:282

        .or_else(|| value.as_str().and_then(|s| s.parse().ok()))
}

fn parse_burn_amount(amount: Option<f64>) -> Result<Option<OperateBurnRate>> {
    let Some(amount) = amount else {
        return Ok(None);
    };
    if !amount.is_finite() || amount <= 0.0 {
        anyhow::bail!("Burn rate is optional. When set, it must be a positive $/hr.");
    }
    if amount > 10_000.0 {
        anyhow::bail!("Burn rate must be 10000 $/hr or less.");
    }
    let rounded = (amount * 100.0).round() / 100.0;
    if rounded <= 0.0 {
        // A sub-cent rate rounds to a $0/hr target, which the pace governor
        // would treat as unbounded — reject it instead of silently dropping
        // the requested cap.
        anyhow::bail!("Burn rate must be at least $0.01/hr.");
    }
    Ok(Some(OperateBurnRate {
        kind: "usd_per_hour".to_string(),
        amount_usd_per_hour: rounded,
    }))
}

fn derive_status(op: &Operation) -> (OperateStatus, Option<OperateIdleReason>) {
    if op.status == OperateStatus::Cancelled {
        return (OperateStatus::Cancelled, None);
    }
    if !op.credentials_present {
        return (
            OperateStatus::IdleBlocked,
            Some(OperateIdleReason::MissingCredentials),
        );
    }
    if op.direction.is_empty() {

View on GitHub (pinned to 73e0f67d83)