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
- 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
- Inspect the session token: run `atuin acct current` / re-login with `atuin logout && atuin login -u <user>` to regenerate a clean token
- Check `extra_headers` in `~/.config/atuin/config.toml` for invalid header names/values and quote/trim them
- 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
- Use `?` (or `.wrap_err(...)`) instead of `.expect` for any fallible constructor inside a function that already returns Result
- Regenerate the session token via `atuin logout && atuin login` after manual key/token edits; never paste tokens with trailing whitespace
- Lint config.toml `extra_headers` entries at startup (name charset: token chars; value: visible ASCII) so mistakes surface at config load, not mid-command
- Smoke-test `atuin store push --force` in CI/container images to catch TLS-backend build failures before users do
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
- Failed to compute stats
- invalid id UUID format in sqlite DB
- failed to parse uuid for local store status
- Empty theme directory override and could not find theme else
- Failed to deserialize theme: {}
AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16).
Data as JSON: /api/errors/dcb09dd325a53de0.
Report an issue: GitHub.