atuinsh/atuin · error

Hub login failed with status

Error message

Hub login failed with status {status}

What it means

Final fallback in `HubAuthClient::login`: any non-success status that is not 403-with-parseable-body and not 401 becomes this bail embedding the HTTP status. It covers 3xx redirections, 404, 429, and all 5xx responses from the Hub or intermediaries.

Solutions

  1. Check status embedded in the message: 4xx means fix the request/URL, 5xx means server-side
  2. Verify the configured Hub address is correct and reachable (`curl https://api.atuin.sh/api/v0/login`)
  3. Wait and retry with backoff if the status was 429 or 5xx (check https://status for Hub incidents)
  4. Bypass proxies/VPN to rule out intermediary interference
  5. Update the Atuin CLI in case the login API path changed

Example fix

// before: opaque status-only bail
bail!("Hub login failed with status {status}");
// after: surface response body for diagnosis
let body = resp.text().await.unwrap_or_default();
bail!("Hub login failed with status {status}: {body}");
Defensive patterns

Strategy: retry

Validate before calling

// Check Hub reachability before login
let status = reqwest::get("https://api.atuin.sh/health").await?.status();
if !status.is_success() {
    bail!("Hub unreachable/unhealthy: {status}");
}

Type guard

fn is_unmapped_login_status(err: &Report) -> Option<u16> {
    let msg = err.to_string();
    msg.strip_prefix("Hub login failed with status ")
        .and_then(|s| s.split(' ').next())
        .and_then(|code| code.parse().ok())
}

Try / catch

match hub.login(username, password, totp).await {
    Err(e) if e.to_string().starts_with("Hub login failed with status") => {
        let retryable = e.to_string().contains("429")
            || e.to_string().contains("5"); // 5xx
        if retryable {
            tokio::time::sleep(Duration::from_secs(10)).await;
            // retry with backoff
        } else {
            eprintln!("fix Hub address/request: {e}");
        }
    }
    other => { /* handle normally */ }
}

Prevention

When it happens

Trigger: POST `api/v0/login` returns e.g. 404 (wrong base URL/path), 429 (rate limited), 500/502/503 (Hub or proxy outage), or an unexpected redirect.

Common situations: Misconfigured Hub address (wrong host/path producing 404); Hub service disruption (5xx); aggressive rate limiting after repeated failed logins; corporate proxy or captive portal intercepting the request.

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/66787f59b12aa306. Report an issue: GitHub.

Appendix: source

Thrown at crates/atuin-client/src/auth.rs:319

                session: login.session,
                auth_type: login.auth,
            });
        }

        if status == StatusCode::FORBIDDEN
            && let Ok(err) = resp.json::<HubErrorResponse>().await
        {
            if err.code.as_deref() == Some("2fa_required") {
                return Ok(AuthResponse::TwoFactorRequired);
            }
            bail!("{}", err.reason);
        }

        if status == StatusCode::UNAUTHORIZED {
            bail!("invalid credentials");
        }

        bail!("Hub login failed with status {status}");
    }

    async fn register(&self, username: &str, email: &str, password: &str) -> Result<AuthResponse> {
        let url = self.address.append_path("api/v0/register")?;
        let client = reqwest::Client::new();

        let resp = client
            .post(url)
            .header(USER_AGENT, APP_USER_AGENT)
            .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION)
            .json(&serde_json::json!({
                "email": email,
                "username": username,
                "password": password,
            }))
            .send()
            .await
            .context("failed to connect to Atuin Hub")?;

View on GitHub (pinned to 900007c480)