zeroclaw-labs/zeroclaw · critical
failed to build HTTP client
Error message
failed to build HTTP client
What it means
GmailPushChannel::new builds a reqwest Client with a 30s timeout and calls .expect("failed to build HTTP client") (gmail_push.rs:169-172), so a builder failure panics inside the constructor instead of returning an error. reqwest's Client::builder().build() essentially only fails when the TLS backend cannot initialize (missing/broken native-tls/OpenSSL linkage, no system CA store, or a rustls feature misconfiguration); the timeout setting cannot cause it. Because new() returns Self rather than Result, the panic escapes to whoever wires the channel — typically crashing the gateway at startup. This also violates the repository's own AGENTS.md rule against expect() on production paths; upstream should propagate the error.
Source
Thrown at crates/zeroclaw-channels/src/gmail_push.rs:172
/// Resolves inbound external peers from canonical state at message-time.
/// No cache (see AGENTS.md "ABSOLUTE RULE — SINGLE SOURCE OF TRUTH").
pub peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync>,
http: Client,
last_history_id: Arc<Mutex<u64>>,
/// Sender half injected by the gateway to forward webhook-received messages.
pub tx: Arc<Mutex<Option<mpsc::Sender<ChannelMessage>>>>,
}
impl GmailPushChannel {
pub fn new(
config: GmailPushConfig,
alias: impl Into<String>,
peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync>,
) -> Self {
let http = Client::builder()
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build HTTP client");
Self {
config,
alias: alias.into(),
peer_resolver,
http,
last_history_id: Arc::new(Mutex::new(0)),
tx: Arc::new(Mutex::new(None)),
}
}
/// Register a Gmail watch subscription via `POST /gmail/v1/users/me/watch`.
pub async fn register_watch(&self) -> Result<WatchResponse> {
let token = self.config.oauth_token.clone();
if token.is_empty() {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),View on GitHub (pinned to 88bb9c8533)
Solutions
- Fix the TLS environment: install ca-certificates and the OpenSSL libraries the binary was linked against, or build zeroclaw-channels with rustls-based reqwest features
- Reproduce outside the app with a tiny probe binary that just calls reqwest::Client::builder().build() to confirm it is environmental
- Patch upstream: make new() return anyhow::Result<Self> (or inject a pre-built Client) so the failure is an error, not a panic — the repo's AGENTS.md forbids expect() on production paths
- As a stopgap, wrap channel construction in std::panic::catch_unwind at startup and fail with a diagnostic instead of an opaque panic
Example fix
// before — crates/zeroclaw-channels/src/gmail_push.rs
let http = Client::builder()
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build HTTP client");
// after — propagate instead of panicking (change new() to return anyhow::Result<Self>)
let http = Client::builder()
.timeout(Duration::from_secs(30))
.build()
.map_err(|e| anyhow::anyhow!("failed to build Gmail HTTP client: {e}"))?; Defensive patterns
Strategy: validation
Validate before calling
// probe the TLS/HTTP stack before wiring the channel
fn http_stack_ok() -> bool {
reqwest::Client::builder().build().is_ok()
} Try / catch
// last-resort containment until new() returns Result
let channel = std::panic::catch_unwind(|| {
GmailPushChannel::new(config, alias.clone(), peer_resolver)
});
match channel {
Ok(c) => c,
Err(_) => { /* log TLS backend failure; skip or abort channel startup with a diagnostic */ }
} Prevention
- Ship ca-certificates plus the OpenSSL libs the binary links against (or build with rustls features) in every deployment image
- Add a startup doctor check that smoke-tests reqwest::Client::builder().build() before wiring channels
- Prefer rustls-based builds for portable/static binaries
- File or carry an upstream change making GmailPushChannel::new return Result — expect() on a production path violates this repo's own AGENTS.md
When it happens
Trigger: Constructing GmailPushChannel during gateway channel setup for a configured [channels.gmail.<alias>] on a host whose TLS dependencies fail to load: libssl absent or version-mismatched, no CA certificates, or a cross-compiled binary built without the matching TLS features.
Common situations: Slim or from-scratch Docker images lacking ca-certificates/libssl; musl cross-builds linked against native-tls; OS upgrade breaking OpenSSL 1.1 vs 3 linkage; Nix/static targets with wrong TLS features enabled.
Related errors
- failed to build reqwest client
- failed to build webhook HTTP client
- HTTP client build
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/c4b4a57b6827a660.
Report an issue: GitHub.