atuinsh/atuin · error

failed to create client

Error message

failed to create client

What it means

A panic from `.expect()` on `Client::new(...)` in the `--force` path of `atuin store push` (push.rs:50-59). `Client::new` (atuin-client/src/api_client.rs:272-306) is fallible: it re-validates the configured `extra_headers` (`extra_headers_map` rejects invalid header names/values), parses the `Authorization` header from `settings.sync_auth_token()` (rejects tokens containing non-visible-ASCII characters such as trailing newlines), and calls `reqwest::ClientBuilder::build()`, which can fail if the TLS backend cannot initialize. Because the code uses `.expect` instead of propagating with `?`, any of these construction errors aborts the process with a panic instead of the CLI's normal error reporting.

Source

Thrown at crates/atuin/src/command/client/store/push.rs:59

        if self.force {
            println!("Forcing remote store overwrite!");
            println!("Clearing remote store");

            let caps = atuin_client::api_client::caps_client(
                &settings.sync_address,
                &settings.extra_headers,
            )?;
            let client = Client::new(
                settings.sync_address.clone(),
                settings.sync_auth_token().await?,
                settings.network_connect_timeout,
                settings.network_timeout * 10, // we may be deleting a lot of data... so up the
                // timeout
                &settings.extra_headers,
                caps,
            )
            .expect("failed to create client");

            client.delete_store().await?;
        }

        // We can actually just use the existing diff/etc to push
        // 1. Diff
        // 2. Get operations
        // 3. Filter operations by
        //  a) are they an upload op?
        //  b) are they for the host/tag we are pushing here?
        let client = sync::build_client(settings).await?;
        let (diff, remote_index) = sync::diff(&client, &store).await?;

        let key = paseto_v4::Key::try_load_from_path(&settings.key_path)?;

        // Skip on --force: that path intentionally replaces remote with local.
        if !self.force {
            sync::check_encryption_key(&client, &remote_index, &key)

View on GitHub (pinned to 202f6ad98e)

Solutions

  1. Replace `.expect("failed to create client")` with `?` — `Push::run` already returns `eyre::Result`, so the underlying error message (which names the offending header or token problem) will be reported instead of a panic
  2. Inspect the session token: run `atuin acct current` / re-login with `atuin logout && atuin login -u <user>` to regenerate a clean token
  3. Check `extra_headers` in `~/.config/atuin/config.toml` for invalid header names/values and quote/trim them
  4. If the error mentions TLS, verify the runtime environment (openssl/rustls availability, SSL_CERT_DIR/SSL_CERT_FILE pointing at a valid cert store)

Example fix

// before
let client = Client::new(
    settings.sync_address.clone(),
    settings.sync_auth_token().await?,
    settings.network_connect_timeout,
    settings.network_timeout * 10,
    &settings.extra_headers,
    caps,
)
.expect("failed to create client");

// after
let client = Client::new(
    settings.sync_address.clone(),
    settings.sync_auth_token().await?,
    settings.network_connect_timeout,
    settings.network_timeout * 10,
    &settings.extra_headers,
    caps,
)?;
Defensive patterns

Strategy: validation

Validate before calling

// Validate config-derived headers and the token BEFORE constructing the client
use reqwest::header::{HeaderName, HeaderValue};
for (name, value) in &settings.extra_headers {
    HeaderName::from_bytes(name.as_bytes())?;   // fails clearly on bad names
    HeaderValue::from_str(value)?;              // fails clearly on bad values
}
HeaderValue::from_str(&format!("Token {}", settings.sync_auth_token().await?))?; // token sanity check

Try / catch

// Client::new already returns Result — propagate it with context instead of expect:
let client = Client::new(addr, token, ct, t, &settings.extra_headers, caps)
    .wrap_err("failed to create sync client — check extra_headers and your session token")?;

Prevention

When it happens

Trigger: Running `atuin store push --force` when (a) the session token in the auth/session store contains invalid header characters (hand-edited, bad paste, corrupted file) so `auth.to_header_value().parse()` fails, (b) `extra_headers` in config.toml has an invalid name/value that wasn't already caught by the preceding `caps_client(...)?` call, or (c) `reqwest` cannot build its client (broken TLS backend / rustls-native-certs environment). Non-force pushes never hit this line.

Common situations: A corrupted or whitespace-padded session token after manual `atuin key`/login fiddling; an `extra_headers` entry with a space in the header name or a control character in the value (e.g. copied from a Cloudflare Access JWT with a newline); running `store push --force` in a minimal container where the TLS backend fails to initialize.

Related errors


AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16). Data as JSON: /api/errors/dcb09dd325a53de0. Report an issue: GitHub.