xai-org/grok-build · error

failed to build shared upload HTTP client

Error message

failed to build shared upload HTTP client

What it means

shared_upload_client lazily builds a cached, HTTP/1.1-only reqwest blocking client tuned for uploads (small pool, short idle timeout, User-Agent set) and .expect()s the build succeeds. Panics indicate the reqwest/TLS stack could not construct the client.

Source

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

/// Without pool eviction, all retries would reuse the same dead connection and fail.
///
/// Settings:
/// - HTTP/1.1 only: a degraded multiplexed HTTP/2 connection can silently drop multipart request bodies.
///   The drops cascade into 400 errors across all concurrent uploads
/// - Small connection pool (2 per host) for parallel chunk uploads
/// - Short idle timeout (10s) to evict stale connections before backoff completes
pub fn shared_upload_client() -> reqwest::Client {
    static UPLOAD_CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
    UPLOAD_CLIENT
        .get_or_init(|| {
            xai_grok_extra_ca::build_reqwest_client(|builder| {
                builder
                    .http1_only()
                    .pool_max_idle_per_host(2)
                    .pool_idle_timeout(std::time::Duration::from_secs(10))
                    .user_agent(process_user_agent_string())
            })
            .expect("failed to build shared upload HTTP client")
        })
        .clone()
}

/// A fresh, pool-less HTTP/1.1 [`reqwest::Client`], deliberately not cached: each request opens a new connection.
/// There is no connect timeout; callers bound each request with their own total timeout.
/// The retry policy that uses this client to escape a poisoned pool lives on `send_with_retry_escaping_pool`.
/// The build can fail under file-descriptor or TLS pressure; the caller must not panic on error (the fallback lives at the call site).
pub(crate) fn fresh_http1_client() -> reqwest::Result<reqwest::Client> {
    xai_grok_extra_ca::build_reqwest_client(|builder| {
        builder
            .http1_only()
            .pool_max_idle_per_host(0)
            .user_agent(process_user_agent_string())
    })
}

/// Joins an error's `source()` chain into one string.

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Align rustls/aws-lc-rs feature flags workspace-wide and rebuild
  2. Validate the env-derived client info (origin_client_info_from_env) that feeds process_user_agent_string()
  3. Pre-warm the upload client during startup so failures surface with full context, not deep in an upload path
  4. Pass a prebuilt client where the API allows instead of relying on the shared initializer

Example fix

// before
let client = shared_upload_client(); // first call may panic
// after
// at startup
let _ = shared_upload_client(); // fail fast with clear context
// later in upload path
let client = shared_upload_client();
Defensive patterns

Strategy: try-catch

Validate before calling

let client = std::panic::catch_unwind(xai_grok_http::shared_upload_client)
    .map_err(|_| anyhow!("shared upload client build failed (TLS init?)"))?;

Type guard

null

Try / catch

std::panic::catch_unwind(|| {
    let c = shared_upload_client();
    c.post(url).body(body).send()
})
.map_err(|_| anyhow!("upload HTTP stack unavailable"))?

Prevention

When it happens

Trigger: First call to shared_upload_client() when the crypto provider cannot initialize, CA material fails to load, process_user_agent_string() yields a configuration reqwest rejects, or the builder options are invalid in this build.

Common situations: Same environment issues as other shared clients: mismatched rustls features, stripped containers, conflicting global crypto provider; also an invalid user-agent string from env-derived origin info.

Related errors


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