{"record":{"id":"d4e1238e7003da9b","repo":"gitbutlerapp/gitbutler","slug":"failed-to-build-http-client","errorCode":null,"errorMessage":"failed to build HTTP client","messagePattern":"failed to build HTTP client","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/gitbutler-user/src/api.rs","lineNumber":22,"sourceCode":"//! so that browser-based frontends don't need to make cross-origin requests.\n//! They are also usable from the CLI (`but auth`) without any web framework dependency.\n//!\n//! The public API is synchronous — async HTTP calls are executed on a dedicated\n//! thread with a short-lived Tokio runtime, following the same pattern as `but-forge`.\n\nuse std::time::Duration;\n\nuse anyhow::{Context, Result};\nuse but_path::AppChannel;\nuse reqwest::StatusCode;\nuse serde::{Deserialize, Serialize};\n\nfn http_client() -> reqwest::Client {\n    reqwest::Client::builder()\n        .connect_timeout(Duration::from_secs(10))\n        .timeout(Duration::from_secs(30))\n        .build()\n        .expect(\"failed to build HTTP client\")\n}\n\n/// Error returned when the upstream API rejects a request with an HTTP error status.\n#[derive(Debug, thiserror::Error)]\n#[error(\"API request failed ({status}): {body}\")]\npub struct ApiHttpError {\n    pub status: StatusCode,\n    pub body: String,\n}\n\n/// Returns the GitButler API base URL.\n///\n/// Resolution order:\n/// 1. `GITBUTLER_API_URL` env var at runtime (backend-specific escape hatch)\n/// 2. `PUBLIC_API_BASE_URL` env var at runtime (shared with the desktop frontend)\n/// 3. Compile-time [`AppChannel`]:\n///    - `Release` / `Nightly` → `https://app.gitbutler.com`\n///    - `Dev` → `https://app.staging.gitbutler.com`","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-user/src/api.rs#L4-L40","documentation":"`reqwest::Client::builder().build()` creates the HTTP client including its TLS backend. It fails before any network I/O when the TLS stack cannot initialize: missing or broken system OpenSSL for native-tls, no installed crypto provider with rustls, or a broken resolver setup. The `.expect` in `http_client()` panics the first time any gitbutler-user API call builds a client.","triggerScenarios":"Any function in crates/gitbutler-user/src/api.rs that calls http_client() on a machine where the builder fails: native-tls cannot load libssl or the CA bundle, two rustls crypto providers are linked with none installed process-wide, or resolver initialization errors out.","commonSituations":"Linux with missing or version-mismatched libssl; static musl builds without vendored OpenSSL; adding a second rustls-provider crate so Client::build() errors with 'no process-level CryptoProvider installed'; minimal containers lacking /etc/ssl/certs.","solutions":["Return Result from http_client() and propagate with .context(\"failed to build HTTP client\") so callers degrade instead of panicking","With rustls: install a default provider once at startup (rustls::crypto::ring::default_provider().install_default()) or use reqwest's default features","With native-tls: vendor OpenSSL via the openssl crate's \"vendored\" feature to remove the system-library dependency","On minimal images, install ca-certificates and run update-ca-certificates"],"exampleFix":"// before\nfn http_client() -> reqwest::Client {\n    reqwest::Client::builder()\n        .connect_timeout(Duration::from_secs(10))\n        .timeout(Duration::from_secs(30))\n        .build()\n        .expect(\"failed to build HTTP client\")\n}\n\n// after\nfn http_client() -> Result<reqwest::Client> {\n    reqwest::Client::builder()\n        .connect_timeout(Duration::from_secs(10))\n        .timeout(Duration::from_secs(30))\n        .build()\n        .context(\"failed to build HTTP client\")\n}","handlingStrategy":"validation","validationCode":"// rustls: install a process-wide crypto provider before the first client build\nfn ensure_tls_provider() {\n    let _ = rustls::crypto::ring::default_provider().install_default();\n}","typeGuard":null,"tryCatchPattern":"match reqwest::Client::builder().timeout(Duration::from_secs(30)).build() {\n    Ok(client) => client,\n    Err(e) => return Err(anyhow::Error::new(e).context(\"failed to build HTTP client\")),\n}","preventionTips":["Build the Client once (for example in a OnceLock) and reuse it","Pin one TLS feature set for reqwest across the workspace","Smoke-test HTTP on the exact base image you ship"],"tags":["rust","reqwest","http-client","tls","panic"],"backgroundTag":"http-client-init-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}