Hmbown/CodeWhale · error

Burn rate is optional. When set, it must be a positive $/hr.

Error message

Burn rate is optional. When set, it must be a positive $/hr.

What it means

`parse_burn_rate` accepts an optional burn rate from a JSON patch (checked under several key aliases). If none of the keys yields a numeric value even though a burn-rate key was present with a non-numeric value, it bails with this message clarifying the field is optional but must be positive when supplied. The actual positivity check happens downstream in `parse_burn_amount`.

Solutions

  1. Send the burn rate as a JSON number, not a string: `"burnRate": 12.5`
  2. Omit the burn-rate key entirely if you don't want to set it (it is optional)
  3. Fix the key typo so the intended value is picked up
  4. Use `normalize_burn_rate` on raw user input before applying the patch

Example fix

// before
{"burnRate": "12.5"}
// after
{"burnRate": 12.5}
Defensive patterns

Strategy: validation

Validate before calling

fn valid_burn_rate(v: &serde_json::Value) -> bool {
    v.is_number() && v.as_f64().map(|n| n.is_finite() && n > 0.0).unwrap_or(false)
}

Type guard

fn as_burn_amount(v: &serde_json::Value) -> Option<f64> {
    v.as_f64().filter(|n| n.is_finite() && *n > 0.0)
}

Try / catch

match parse_burn_rate(&patch) {
    Err(e) if e.to_string().contains("must be a positive") => {
        eprintln!("burnRate must be a JSON number > 0, or omit it.");
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: `apply_operate_patch` passes an operate patch whose `burnRate`/`usdPerHour`/`amount` value is present but `json_number` cannot extract a number (string, null, object, etc.), leaving `amount.is_none()` true while a key existed.

Common situations: Hand-editing a patch and writing the burn rate as a string (`"12.5"` instead of `12.5`); setting the field to `null` expecting 'unset'; UI sending wrong JSON type; key typo producing Null via `.get`.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/06e9ee437365003a. Report an issue: GitHub.

Appendix: source

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

    {
        return Ok(None);
    }
    if let Some("unbounded") = value.get("kind").and_then(|kind| kind.as_str()) {
        return Ok(None);
    }
    let amount = if value.is_number() || value.is_string() {
        json_number(value)
    } else {
        json_number(
            value
                .get("amountUsdPerHour")
                .or_else(|| value.get("usdPerHour"))
                .or_else(|| value.get("amount"))
                .unwrap_or(&serde_json::Value::Null),
        )
    };
    if amount.is_none() {
        anyhow::bail!("Burn rate is optional. When set, it must be a positive $/hr.");
    }
    parse_burn_amount(amount)
}

fn json_number(value: &serde_json::Value) -> Option<f64> {
    value
        .as_f64()
        .or_else(|| value.as_i64().map(|n| n as f64))
        .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.");
    }

View on GitHub (pinned to 433685b202)