{"record":{"id":"5594715111a4e79b","repo":"block/buzz","slug":"http-client-build-must-succeed","errorCode":null,"errorMessage":"HTTP client build must succeed","messagePattern":"HTTP client build must succeed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-workflow/src/executor.rs","lineNumber":974,"sourceCode":"    let body_text = String::from_utf8_lossy(&body_bytes).into_owned();\n\n    Ok(serde_json::json!({\n        \"status\": status,\n        \"body\": body_text,\n    }))\n}\n\n/// Returns a shared `reqwest::Client` reused across all workflow HTTP calls.\n/// Sharing a single client reuses the underlying connection pool.\n#[cfg(feature = \"reqwest\")]\nfn shared_http_client() -> &'static reqwest::Client {\n    use std::sync::LazyLock;\n    use std::time::Duration;\n    static CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {\n        reqwest::Client::builder()\n            .timeout(Duration::from_secs(10))\n            .build()\n            .expect(\"HTTP client build must succeed\")\n    });\n    &CLIENT\n}\n\n/// POST `{\"emoji\": emoji}` to `POST /api/messages/{message_id}/reactions`.\n#[cfg(feature = \"reqwest\")]\nasync fn add_reaction_impl(message_id: &str, emoji: &str) -> Result<JsonValue, WorkflowError> {\n    let base_url =\n        std::env::var(\"BUZZ_RELAY_BASE_URL\").unwrap_or_else(|_| \"http://localhost:3000\".to_owned());\n\n    let url = format!(\"{base_url}/api/messages/{message_id}/reactions\");\n\n    let client = shared_http_client();\n\n    let mut req = client\n        .post(&url)\n        .header(\"Content-Type\", \"application/json\")\n        .json(&serde_json::json!({ \"emoji\": emoji }));","sourceCodeStart":956,"sourceCodeEnd":992,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-workflow/src/executor.rs#L956-L992","documentation":"Workflow HTTP steps share one static LazyLock reqwest::Client with a 10s timeout. If Client::builder().build() fails (essentially only TLS-backend initialization), this expect panics — and because the client lives in a static LazyLock, the panic poisons it: every subsequent shared_http_client() call re-panics, permanently breaking all workflow HTTP steps in the process.","triggerScenarios":"reqwest with the native-tls backend in an environment where OpenSSL cannot initialize; after the first failure any workflow invoking add_reaction or other HTTP tool calls panics its task even if the environment problem was transient.","commonSituations":"Minimal container images missing TLS runtime pieces; mixed TLS backends in the dependency tree; misconfigured SSL_CERT_FILE-style environment variables breaking OpenSSL init.","solutions":["Enable reqwest's rustls TLS backend in buzz-workflow so it matches the ring provider the relay installs","Build the client once during engine startup, store it in the executor state, and return Result so failures are reported once instead of poisoning a static","Verify TLS prerequisites in the runtime image (CA bundle, OpenSSL versions) before shipping"],"exampleFix":"// before\nstatic CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {\n    reqwest::Client::builder()\n        .timeout(Duration::from_secs(10))\n        .build()\n        .expect(\"HTTP client build must succeed\")\n});\n\n// after — construct once at startup, propagate the failure\nlet client = reqwest::Client::builder()\n    .timeout(Duration::from_secs(10))\n    .build()\n    .map_err(|e| WorkflowError::HttpInit(e.to_string()))?;","handlingStrategy":"validation","validationCode":"// build once at executor startup and store in the engine\nlet client = reqwest::Client::builder()\n    .timeout(Duration::from_secs(10))\n    .build()\n    .map_err(|e| format!(\"workflow HTTP client init failed: {e}\"))?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid expect() inside LazyLock/statics — a poisoned static turns one transient failure into a permanent one","Initialize shared HTTP clients during component startup where errors can propagate","Use the rustls backend consistently across the workspace to make client construction effectively infallible"],"tags":["rust","reqwest","tls","workflow","lazy-initialization","static"],"backgroundTag":"http-client-init-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}