{"record":{"id":"a58c879d7fb573be","repo":"databendlabs/databend","slug":"heartbeat-request-should-contain-auth-header","errorCode":null,"errorMessage":"heartbeat request should contain auth header","messagePattern":"heartbeat request should contain auth header","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/service/src/servers/http/v1/http_query_handlers.rs","lineNumber":988,"sourceCode":"    if num_task > 0 {\n        let mut tasks = Vec::with_capacity(num_task);\n        let uri = req.uri().to_string();\n        let method = req.method();\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            http::header::CONTENT_TYPE,\n            HeaderValue::from_static(\"application/json\"),\n        );\n        let agent = format!(\"databend-query/{}\", ctx.version.semantic);\n        headers.insert(\n            http::header::USER_AGENT,\n            HeaderValue::from_str(&agent).unwrap(),\n        );\n        headers.insert(\n            http::header::AUTHORIZATION,\n            req.headers()\n                .get(http::header::AUTHORIZATION)\n                .expect(\"heartbeat request should contain auth header\")\n                .to_owned(),\n        );\n        for (node, body) in nodes_to_forwards {\n            let uri = uri.clone();\n            let method = method.clone();\n            let headers = headers.clone();\n\n            tasks.push(async move {\n                match forward_request_with_body(node, &uri, body, method, headers).await {\n                    Ok(mut resp) => {\n                        if resp.status() == StatusCode::OK {\n                            Some(\n                                resp.take_body()\n                                    .into_json::<HeartBeatResponse>()\n                                    .await\n                                    .unwrap(),\n                            )\n                        } else {","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/servers/http/v1/http_query_handlers.rs#L970-L1006","documentation":"The HTTP heartbeat handler forwards heartbeat requests to other cluster nodes, and it copies the incoming request's `AUTHORIZATION` header into the forwarded headers via `.expect(\"heartbeat request should contain auth header\")`. If the client's heartbeat request lacks an Authorization header, this panics inside the axum/poem handler. Databend assumes heartbeats always carry credentials because cluster communication is authenticated, so a bare heartbeat is treated as a bug in the caller.","triggerScenarios":"Sending `PUT /v1/heartbeat` (or the configured heartbeat endpoint) without an `Authorization` header, e.g., a custom health-check script, load balancer probe, or monitoring agent hitting the endpoint directly with no `--user`/auth configured.","commonSituations":"Kubernetes liveness/readiness probes configured without basic-auth; curl one-liners omitting `-u user:password`; ops scripts written against an older deployment where auth was disabled; reverse proxies stripping the Authorization header.","solutions":["Add credentials to the heartbeat caller: use `curl -u user:password ...` or set basic-auth in the probe/script configuration.","Fix Kubernetes probes/monitoring to embed the auth header (or use a separate unauthenticated health endpoint if available).","Check any proxy/load balancer in front of Databend to ensure it does not strip the Authorization header.","Harden the handler to return 401 instead of panicking when the header is absent (wrap in `ok_or_else` → `ErrorCode`/HTTP error response)."],"exampleFix":"// before\nreq.headers()\n    .get(http::header::AUTHORIZATION)\n    .expect(\"heartbeat request should contain auth header\")\n    .to_owned(),\n\n// after\nreq.headers()\n    .get(http::header::AUTHORIZATION)\n    .ok_or_else(|| ErrorCode::Unauthenticated(\n        \"heartbeat request should contain auth header\"\n    ))?\n    .to_owned(),","handlingStrategy":"validation","validationCode":"// Client-side check before sending heartbeat\ncurl -s -o /dev/null -w \"%{http_code}\" -u \"${DATABEND_USER}:${DATABEND_PASSWORD}\" \\\n  http://host:8080/v1/heartbeat || echo \"heartbeat auth missing/failed\"","typeGuard":"function heartbeatRequestHasAuth(headers) {\n  return typeof headers.authorization === 'string' && headers.authorization.length > 0;\n}","tryCatchPattern":"// Server-side hardening\nlet auth = match req.headers().get(http::header::AUTHORIZATION) {\n    Some(h) => h.to_owned(),\n    None => return Ok((StatusCode::UNAUTHORIZED, \"heartbeat requires auth header\")),\n};","preventionTips":["Always configure basic-auth in heartbeat clients: curl -u user:password.","Embed credentials in Kubernetes liveness/readiness probe commands.","Verify reverse proxies forward the Authorization header unchanged.","Use a dedicated unauthenticated health endpoint for infrastructure probes if one exists."],"tags":["http","authentication","panic","heartbeat","api"],"backgroundTag":"authentication-required","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}