{"record":{"id":"acbed6d4d115b36f","repo":"BloopAI/vibe-kanban","slug":"failed-to-build-releases-http-client","errorCode":null,"errorMessage":"failed to build releases HTTP client","messagePattern":"failed to build releases HTTP client","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/server/src/routes/releases.rs","lineNumber":26,"sourceCode":"use serde::{Deserialize, Serialize};\nuse tokio::sync::RwLock;\n\nuse crate::DeploymentImpl;\n\nconst CACHE_TTL: Duration = Duration::from_secs(15 * 60);\nconst GITHUB_API_URL: &str = \"https://api.github.com/repos/BloopAI/vibe-kanban/releases\";\n\ntype ReleasesCache = RwLock<Option<(Vec<GitHubRelease>, Instant)>>;\n\nstatic HTTP_CLIENT: OnceLock<Client> = OnceLock::new();\nstatic RELEASES_CACHE: OnceLock<ReleasesCache> = OnceLock::new();\n\nfn client() -> &'static Client {\n    HTTP_CLIENT.get_or_init(|| {\n        Client::builder()\n            .user_agent(\"vibe-kanban-server\")\n            .build()\n            .expect(\"failed to build releases HTTP client\")\n    })\n}\n\nfn cache() -> &'static RwLock<Option<(Vec<GitHubRelease>, Instant)>> {\n    RELEASES_CACHE.get_or_init(|| RwLock::new(None))\n}\n\npub fn router() -> Router<DeploymentImpl> {\n    Router::new().route(\"/releases\", get(get_releases))\n}\n\n#[derive(Debug, Serialize, Deserialize, Clone)]\npub struct GitHubRelease {\n    pub name: String,\n    pub tag_name: String,\n    pub published_at: String,\n    pub body: String,\n}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/server/src/routes/releases.rs#L8-L44","documentation":"This panic comes from the lazy initialization of the shared reqwest HTTP client used to fetch GitHub releases. `Client::builder().build()` returns a `Result` and only fails when the TLS backend cannot be initialized (or other builder settings are invalid, none of which are set here). Because the client is built via `OnceLock::get_or_init`, the panic fires on the first request to the /releases route, not at server startup.","triggerScenarios":"The first call to `client()` — i.e. the first GET to the `/releases` endpoint after cache miss — invokes `Client::builder().user_agent(\"vibe-kanban-server\").build()`, which returns `Err` and trips the `.expect()`. The only realistic cause with just a user_agent configured is TLS backend (rustls/native-tls) initialization failure.","commonSituations":"Running the server binary in a stripped-down container or minimal Linux environment where the TLS backend's required root certificates or crypto library are missing; cross-compiled builds where native-tls/OpenSSL was not linked correctly; environments with no CA certificates installed so the TLS root store cannot be built.","solutions":["Check the panic's underlying error (reqwest build error) to identify whether TLS/crypto backend initialization failed","If using native-tls, install system CA certificates (e.g. `ca-certificates` package) or set SSL_CERT_FILE, or switch the client to the rustls-tls feature with `Client::builder().use_rustls_tls()`","Rebuild the binary with the `rustls-tls` reqwest feature to avoid OpenSSL linking problems in cross-compiled or musl builds","Replace the `.expect()` with graceful error handling so a failed client build returns a 503 from the route instead of panicking","Verify the app runs in the same environment it was built/tested for (glibc vs musl, OpenSSL versions)"],"exampleFix":"// before\nfn client() -> &'static Client {\n    HTTP_CLIENT.get_or_init(|| {\n        Client::builder()\n            .user_agent(\"vibe-kanban-server\")\n            .build()\n            .expect(\"failed to build releases HTTP client\")\n    })\n}\n// after\nfn client() -> Option<&'static Client> {\n    HTTP_CLIENT.get_or_init(|| {\n        Client::builder()\n            .user_agent(\"vibe-kanban-server\")\n            .build()\n            .ok()\n    })\n} // route handler returns 503 when client() is None","handlingStrategy":"fallback","validationCode":"// Preflight TLS availability before relying on the releases client:\nfn tls_available() -> bool {\n    reqwest::Client::builder()\n        .user_agent(\"vibe-kanban-server\")\n        .build()\n        .is_ok()\n}","typeGuard":null,"tryCatchPattern":"// In Rust, catch the panic or avoid it entirely by handling the Result:\nlet client = reqwest::Client::builder()\n    .user_agent(\"vibe-kanban-server\")\n    .build();\nmatch client {\n    Ok(c) => /* use c */,\n    Err(e) => tracing::error!(\"releases HTTP client unavailable: {}\", e), // serve 503\n}","preventionTips":["Use reqwest's rustls-tls feature to avoid native OpenSSL dependency issues","Ensure CA certificates are installed in container/minimal images","Wrap fallible OnceLock initialization in Result or Option instead of expect","Test the binary in the deployment environment (distro, musl/glibc) before release"],"tags":["http-client","tls","panic","reqwest"],"backgroundTag":"tls-backend-init-failed","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}