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

  1. Return Result from http_client() and propagate with .context("failed to build HTTP client") so callers degrade instead of panicking
  2. With rustls: install a default provider once at startup (rustls::crypto::ring::default_provider().install_default()) or use reqwest's default features
  3. With native-tls: vendor OpenSSL via the openssl crate's "vendored" feature to remove the system-library dependency
  4. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/d4e1238e7003da9b. Report an issue: GitHub.