gitbutlerapp/gitbutler · critical
failed to build HTTP client
Error message
failed to build HTTP client
What it means
`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.
Source
Thrown at crates/gitbutler-user/src/api.rs:22
//! so that browser-based frontends don't need to make cross-origin requests.
//! They are also usable from the CLI (`but auth`) without any web framework dependency.
//!
//! The public API is synchronous — async HTTP calls are executed on a dedicated
//! thread with a short-lived Tokio runtime, following the same pattern as `but-forge`.
use std::time::Duration;
use anyhow::{Context, Result};
use but_path::AppChannel;
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
fn http_client() -> reqwest::Client {
reqwest::Client::builder()
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build HTTP client")
}
/// Error returned when the upstream API rejects a request with an HTTP error status.
#[derive(Debug, thiserror::Error)]
#[error("API request failed ({status}): {body}")]
pub struct ApiHttpError {
pub status: StatusCode,
pub body: String,
}
/// Returns the GitButler API base URL.
///
/// Resolution order:
/// 1. `GITBUTLER_API_URL` env var at runtime (backend-specific escape hatch)
/// 2. `PUBLIC_API_BASE_URL` env var at runtime (shared with the desktop frontend)
/// 3. Compile-time [`AppChannel`]:
/// - `Release` / `Nightly` → `https://app.gitbutler.com`
/// - `Dev` → `https://app.staging.gitbutler.com`View on GitHub (pinned to caf1f223d3)
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
Example fix
// before
fn http_client() -> reqwest::Client {
reqwest::Client::builder()
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build HTTP client")
}
// after
fn http_client() -> Result<reqwest::Client> {
reqwest::Client::builder()
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(30))
.build()
.context("failed to build HTTP client")
} Defensive patterns
Strategy: validation
Validate before calling
// rustls: install a process-wide crypto provider before the first client build
fn ensure_tls_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
} Try / catch
match reqwest::Client::builder().timeout(Duration::from_secs(30)).build() {
Ok(client) => client,
Err(e) => return Err(anyhow::Error::new(e).context("failed to build HTTP client")),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- broker already configured
- broker has not been configured
- BUG: {id} is queued
- BUG: It should not be possible to omit sources
- prefiltered
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/d4e1238e7003da9b.
Report an issue: GitHub.