atuinsh/atuin · error
{}
Error message
{} What it means
In `HubAuthClient::login`, a 403 (FORBIDDEN) response whose body parses as `HubErrorResponse` is turned into this bail carrying the server's `reason` string, unless the code is `2fa_required` (which returns TwoFactorRequired instead). The message text is dynamic — it is whatever the Hub server put in `reason` — so it can represent many refusal causes.
Solutions
- Read the `reason` text in the error — it comes verbatim from the Hub and states the specific cause
- Check account status on the Atuin Hub web UI (suspension, verification required)
- Complete email verification or contact Hub support if the account is flagged
- If 2FA is involved, supply a valid `totp_code` so the server doesn't reach a forbidden state
- Update the Atuin CLI if the server indicates the client version is rejected
Example fix
null
Defensive patterns
Strategy: try-catch
Type guard
fn is_hub_forbidden_reason(err: &Report) -> Option<String> {
let msg = err.to_string();
if msg == "invalid credentials" || msg.starts_with("Hub login failed") { None }
else { Some(msg) } // 403 reason text is surfaced verbatim
} Try / catch
match hub.login(username, password, totp).await {
Ok(AuthResponse::Success { .. }) => println!("logged in"),
Ok(AuthResponse::TwoFactorRequired) => {
// prompt for TOTP and retry with the code
}
Err(e) => {
// 403 reason is server-provided: display it and check account status on the Hub
eprintln!("login refused by Hub: {e}");
}
} Prevention
- Read the server `reason` text — it states the exact refusal cause
- Keep the account in good standing (verified email, not suspended) on the Hub
- Supply a TOTP code when the account has 2FA enabled
- Keep the CLI updated so the server does not reject the client version
When it happens
Trigger: POSTing to `api/v0/login` and receiving 403 with a JSON body `{"reason": ..., "code": ...}` where `code != "2fa_required"` — e.g. account disabled/suspended, login forbidden from this client, or any server-side policy rejection.
Common situations: Account suspended or banned on Atuin Hub; organization/IP policy blocking login; server-side maintenance flags; using a client version the server no longer permits (server rejects with explanatory reason).
Related errors
- invalid credentials
- Hub login failed with status
- current password is incorrect
- invalid login details
- password is incorrect
AI-assisted analysis of atuinsh/atuin@900007c480 (2026-09-19).
Data as JSON: /api/errors/19c84ee9e22f8194.
Report an issue: GitHub.
Appendix: source
Thrown at crates/atuin-client/src/auth.rs:312
.context("failed to connect to Atuin Hub")?;
let status = resp.status();
if status.is_success() {
let login: LoginResponse = resp.json().await?;
return Ok(AuthResponse::Success {
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!({View on GitHub (pinned to 900007c480)