zeroclaw-labs/zeroclaw · critical
failed to build reqwest client
Error message
failed to build reqwest client
What it means
The SkillForge scout constructor builds a long-lived HTTP client with default headers and a 30-second timeout. `reqwest::Client::builder().build()` returns Err when the TLS backend cannot initialize (broken OpenSSL install, missing CA store, rustls/native-tls feature mismatch); `.expect()` turns that into a process abort at Scout::new time.
Source
Thrown at crates/zeroclaw-runtime/src/skillforge/scout.rs:92
headers.insert(
reqwest::header::ACCEPT,
"application/vnd.github+json".parse().expect("valid header"),
);
headers.insert(
reqwest::header::USER_AGENT,
"ZeroClaw-SkillForge/0.1".parse().expect("valid header"),
);
if let Some(ref t) = token
&& let Ok(val) = format!("Bearer {t}").parse()
{
headers.insert(reqwest::header::AUTHORIZATION, val);
}
let client = reqwest::Client::builder()
.default_headers(headers)
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build reqwest client");
Self {
client,
queries: vec!["zeroclaw skill".into(), "ai agent skill".into()],
}
}
/// Parse the GitHub search/repositories JSON response.
fn parse_items(body: &serde_json::Value) -> Vec<ScoutResult> {
let items = match body.get("items").and_then(|v| v.as_array()) {
Some(arr) => arr,
None => return vec![],
};
items
.iter()
.filter_map(|item| {
let name = item.get("name")?.as_str()?.to_string();View on GitHub (pinned to 88bb9c8533)
Solutions
- Install or refresh certificates and the TLS library in the runtime image (e.g. `apt-get install -y ca-certificates`) or pin reqwest to the rustls-tls feature in Cargo.toml
- Construct the scout once at process startup so a TLS failure surfaces as a clean startup error rather than a mid-run panic
- Change `Scout::new` to return `Result<Self>` and propagate the build error with `?` instead of `.expect()`
Example fix
// before
let client = reqwest::Client::builder()
.default_headers(headers)
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build reqwest client");
// after
let client = reqwest::Client::builder()
.default_headers(headers)
.timeout(Duration::from_secs(30))
.build()?; // Scout::new returns anyhow::Result<Self> Defensive patterns
Strategy: validation
Validate before calling
fn assert_http_stack_ready() -> anyhow::Result<()> {
reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.build()
.map_err(|e| anyhow::anyhow!("HTTP stack unusable: {e}"))?;
Ok(())
}
// call once at process startup, before any scout is constructed Prevention
- Install ca-certificates in minimal container images and pin the reqwest TLS backend (rustls) to avoid OpenSSL drift
- Construct long-lived HTTP clients once at startup where a failure is a visible, actionable error
When it happens
Trigger: Calling `Scout::new(...)` in a container without CA certificates, after a dependency upgrade changed the reqwest TLS backend, or on a host where the native TLS library cannot load.
Common situations: Minimal Docker images (alpine/distroless) missing ca-certificates; `cargo update` flipping between native-tls and rustls; air-gapped hosts with a broken OpenSSL; CI images without cert bundles.
Related errors
- failed to build HTTP client
- failed to build webhook HTTP client
- HTTP client build
- CLI channel factory not registered — call register_cli_chann
- purge_agent not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a390d6f86b4ebf28.
Report an issue: GitHub.