xai-org/grok-build · critical

failed to build shared blocking HTTP client

Error message

failed to build shared blocking HTTP client

What it means

This panic fires when `reqwest::blocking::Client::build()` fails inside `shared_startup_blocking_client`, the lazy initializer for a process-wide blocking HTTP client used at startup. The builder itself validates configuration (TLS backend, timeout values, user-agent), so a failure here means the client could not be constructed at all — typically an TLS/runtime initialization problem, not a network issue. The `.expect` deliberately aborts startup because no HTTP fetches can proceed without this client.

Source

Thrown at crates/codegen/xai-grok-http/src/lib.rs:579

///
/// Mirrors `shared_client()`'s pool self-healing for the same reason: this client is reused (settings, prefetch).
/// Idle and TCP eviction drops a connection before the upstream idle window (~60-100s; 30s is a conservative default) closes it.
/// The HTTP/2 keepalive-ping setters that `shared_client()` uses are not exposed on reqwest's blocking `ClientBuilder` (0.12).
/// Only the idle and TCP eviction half applies here.
pub fn shared_startup_blocking_client() -> reqwest::blocking::Client {
    static BLOCKING_CLIENT: OnceLock<reqwest::blocking::Client> = OnceLock::new();
    BLOCKING_CLIENT
        .get_or_init(|| {
            let _timer = startup_timer!("startup.http_blocking_client_build");
            xai_grok_extra_ca::build_blocking_reqwest_client(|builder| {
                builder
                    .connect_timeout(STARTUP_FETCH_TIMEOUT)
                    .timeout(STARTUP_FETCH_TIMEOUT)
                    .user_agent(process_user_agent_string())
                    .pool_idle_timeout(std::time::Duration::from_secs(30))
                    .tcp_keepalive(std::time::Duration::from_secs(30))
            })
            .expect("failed to build shared blocking HTTP client")
        })
        .clone()
}

#[allow(clippy::disallowed_methods)] // test clients hit localhost mocks
#[cfg(test)]
mod tests {
    use super::*;

    /// `error_cause_chain` appends each `source()` joined with ": ", so a reqwest error whose `Display` hides the hyper cause still surfaces it.
    #[test]
    fn error_cause_chain_appends_hidden_sources() {
        #[derive(Debug)]
        struct Leaf;
        impl std::fmt::Display for Leaf {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "connection closed before message completed")
            }

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Check the panic's inner source message from reqwest — it names the exact build failure (usually TLS backend or root-store load).
  2. On Linux, ensure the CA bundle exists (ca-certificates installed) or set SSL_CERT_FILE to a valid PEM bundle.
  3. If the failure is TLS-root related, try a build with a vendored root store (rustls-tls with webpki-roots) or embed certs.
  4. Verify the binary runs on a supported platform where tokio's blocking runtime can spawn threads (ulimit/thread limits can starve client construction).

Example fix

// before
.expect("failed to build shared blocking HTTP client")
// after
.map_err(|e| StartupError::HttpClientInit(e.to_string()))?  // or log and fall back to a per-request client
Defensive patterns

Strategy: fallback

Validate before calling

// preflight: ensure a CA bundle is visible before building the client
if std::env::var_os("SSL_CERT_FILE").map_or(true, |p| !std::path::Path::new(&p).exists()) {
    eprintln!("warning: SSL_CERT_FILE missing; client build may fail");
}

Try / catch

// Rust: catch_unwind around lazy init, or make the initializer fallible
let client = std::panic::catch_unwind(shared_startup_blocking_client)
    .map_err(|_| StartupError::HttpClientInit)?;

Prevention

When it happens

Trigger: Calling any startup fetch path that lazily initializes the shared client when reqwest's ClientBuilder rejects the configuration — e.g. native-tls/rustls backend initialization failure, invalid timeout resolution, or TLS root store load failure on the host.

Common situations: Hosts with a broken or missing system certificate store; statically-linked binaries whose TLS backend cannot load roots; exotic platforms where reqwest's blocking runtime (tokio) cannot be initialized; invalid env-var-driven TLS configuration (e.g. bad SSL_CERT_FILE).

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/76b60041006d4727. Report an issue: GitHub.