atuinsh/atuin · error

usage request failed

Error message

usage request failed ({status})

What it means

Fires in fetch_usage when the hub's GET api/cli/usage endpoint responds with a non-success HTTP status. This is not a network failure — the request reached the server, which rejected it (e.g. 401/403 for an expired or invalid bearer token, 429 rate limiting, or 5xx hub-side trouble). The status code is interpolated into the message so callers can distinguish auth problems (fix by re-authenticating) from transient server errors (fix by retrying later). It surfaces when a user refreshes their credit/usage snapshot outside of a chat session, mirroring the credits object normally delivered on the chat done event.

Solutions

  1. Re-authenticate if the status is 401/403
  2. Wait and retry if the status is 429 (rate limit) or 5xx
  3. Check connectivity and any proxy settings
  4. Update Atuin if the endpoint has changed
Defensive patterns

Strategy: retry

Try / catch

match fetch_usage(&client, &token).await {
    Ok(snapshot) => snapshot,
    Err(e) if e.to_string().contains("429") => { tokio::time::sleep(backoff).await; retry().await? }
    Err(e) if e.to_string().contains("401") => { reauth().await?; retry().await? }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `fetch_usage` when the backend returns 4xx/5xx — invalid/expired auth token, rate limiting, or service error.

Common situations: Checking AI usage with a stale session (401), exceeding plan limits (429), or temporary backend outage (5xx).

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of atuinsh/atuin@900007c480 (2026-09-19). Data as JSON: /api/errors/7ba02491697e31c2. Report an issue: GitHub.

Appendix: source

Thrown at crates/atuin-ai/src/usage.rs:106

}

/// Fetch current usage from the hub. Mirrors the `credits` object on the
/// chat `done` event, for refreshing without starting a chat.
pub async fn fetch_usage(endpoint: &reqwest::Url, token: &str) -> Result<UsageSnapshot> {
    let url = endpoint.append_path("api/cli/usage")?;

    let response = reqwest::Client::new()
        .get(url)
        .header(USER_AGENT, crate::stream::APP_USER_AGENT)
        .bearer_auth(token)
        .timeout(Duration::from_secs(10))
        .send()
        .await
        .context("failed to fetch usage")?;

    let status = response.status();
    if !status.is_success() {
        eyre::bail!("usage request failed ({status})");
    }

    response.json::<UsageSnapshot>().await.context("failed to parse usage response")
}

#[cfg(test)]
mod tests {
    use rstest::rstest;

    use super::*;

    #[test]
    fn deserializes_server_payload() {
        // Shape documented in the hub's CliUsageController / credits_payload.
        let json = r#"{
            "period": "calendar_monthly",
            "resets_at": "2026-08-01T00:00:00Z",
            "requests": {"used": 3, "limit": -1},

View on GitHub (pinned to 900007c480)