{"record":{"id":"dcb09dd325a53de0","repo":"atuinsh/atuin","slug":"failed-to-create-client","errorCode":null,"errorMessage":"failed to create client","messagePattern":"failed to create client","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin/src/command/client/store/push.rs","lineNumber":59,"sourceCode":"\n        if self.force {\n            println!(\"Forcing remote store overwrite!\");\n            println!(\"Clearing remote store\");\n\n            let caps = atuin_client::api_client::caps_client(\n                &settings.sync_address,\n                &settings.extra_headers,\n            )?;\n            let client = Client::new(\n                settings.sync_address.clone(),\n                settings.sync_auth_token().await?,\n                settings.network_connect_timeout,\n                settings.network_timeout * 10, // we may be deleting a lot of data... so up the\n                // timeout\n                &settings.extra_headers,\n                caps,\n            )\n            .expect(\"failed to create client\");\n\n            client.delete_store().await?;\n        }\n\n        // We can actually just use the existing diff/etc to push\n        // 1. Diff\n        // 2. Get operations\n        // 3. Filter operations by\n        //  a) are they an upload op?\n        //  b) are they for the host/tag we are pushing here?\n        let client = sync::build_client(settings).await?;\n        let (diff, remote_index) = sync::diff(&client, &store).await?;\n\n        let key = paseto_v4::Key::try_load_from_path(&settings.key_path)?;\n\n        // Skip on --force: that path intentionally replaces remote with local.\n        if !self.force {\n            sync::check_encryption_key(&client, &remote_index, &key)","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin/src/command/client/store/push.rs#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nlet client = Client::new(\n    settings.sync_address.clone(),\n    settings.sync_auth_token().await?,\n    settings.network_connect_timeout,\n    settings.network_timeout * 10,\n    &settings.extra_headers,\n    caps,\n)\n.expect(\"failed to create client\");\n\n// after\nlet client = Client::new(\n    settings.sync_address.clone(),\n    settings.sync_auth_token().await?,\n    settings.network_connect_timeout,\n    settings.network_timeout * 10,\n    &settings.extra_headers,\n    caps,\n)?;","handlingStrategy":"validation","validationCode":"// Validate config-derived headers and the token BEFORE constructing the client\nuse reqwest::header::{HeaderName, HeaderValue};\nfor (name, value) in &settings.extra_headers {\n    HeaderName::from_bytes(name.as_bytes())?;   // fails clearly on bad names\n    HeaderValue::from_str(value)?;              // fails clearly on bad values\n}\nHeaderValue::from_str(&format!(\"Token {}\", settings.sync_auth_token().await?))?; // token sanity check","typeGuard":null,"tryCatchPattern":"// Client::new already returns Result — propagate it with context instead of expect:\nlet client = Client::new(addr, token, ct, t, &settings.extra_headers, caps)\n    .wrap_err(\"failed to create sync client — check extra_headers and your session token\")?;","preventionTips":["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"],"tags":["rust","atuin","sync","reqwest","http-headers","config","panic","cli"],"backgroundTag":"invalid-http-header","analyzedSha":"202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1","analyzedAt":"2026-08-16T19:30:24.731Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}