atuinsh/atuin · error

failed to parse server version

Error message

failed to parse server version: {:?}

What it means

`ensure_version` reads the `x-atuin-version` header from a server response and parses it as a semver Version. If the header value is not valid UTF-8 (or the semver parse fails) the client bails with the underlying header conversion error debug-printed.

Solutions

  1. Inspect the header: `curl -sD - <sync-url> | grep -i atuin-version`
  2. Fix or remove the proxy rule that rewrites the header
  3. Point `sync_address` at a genuine Atuin server
  4. If parsing semver fails, upgrade the server to a version emitting valid semver
Defensive patterns

Strategy: type-guard

Validate before calling

// pre-flight: confirm the server emits a parseable semver version header
let resp = reqwest::get(format!("{sync_address}/health")).await?;
let v = resp.headers().get("x-atuin-version")
    .and_then(|h| h.to_str().ok())
    .ok_or("no version header")?;
semver::Version::parse(v)?; // fails fast here, not mid-login

Type guard

fn valid_version_header(resp: &reqwest::Response) -> Option<semver::Version> {
    resp.headers().get("x-atuin-version")
        .and_then(|h| h.to_str().ok())
        .and_then(|v| semver::Version::parse(v).ok())
}

Prevention

When it happens

Trigger: Any API call routed through `ensure_version` (register, login, record_status) where the version header exists but its bytes are not valid UTF-8 or not valid semver.

Common situations: A reverse proxy injecting a malformed x-atuin-version header, a non-Atuin service on the sync URL, or a corrupted/modified server build.

Understand the failure class

Related errors


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

Appendix: source

Thrown at crates/atuin-client/src/api_client.rs:208

    let client = reqwest::Client::new();

    let resp = client.get(url).header(USER_AGENT, APP_USER_AGENT).send().await?;
    let resp = handle_resp_error(resp).await?;

    let index = resp.json::<IndexResponse>().await?;
    let version = Version::parse(index.version.as_str())?;

    Ok(version)
}

pub fn ensure_version(response: &Response) -> Result<bool> {
    let version = response.headers().get(ATUIN_HEADER_VERSION);

    let version = if let Some(version) = version {
        match version.to_str() {
            Ok(v) => Version::parse(v),
            Err(e) => {
                bail!("failed to parse server version: {:?}", e);
            }
        }
    } else {
        bail!("Server not reporting its version: it is either too old or unhealthy");
    }?;

    // If the client is newer than the server
    if version.major < ATUIN_VERSION.major {
        println!(
            "Atuin version mismatch! In order to successfully sync, the server needs to run a \
             newer version of Atuin"
        );
        println!("Client: {ATUIN_CARGO_VERSION}");
        println!("Server: {version}");

        return Ok(false);
    }

View on GitHub (pinned to 900007c480)