Skip to content

Distributor: return HTTP 499 instead of 500 on client push cancellation#7717

Open
AllenYTF wants to merge 2 commits into
cortexproject:masterfrom
AllenYTF:return-499-on-client-push-cancellation
Open

Distributor: return HTTP 499 instead of 500 on client push cancellation#7717
AllenYTF wants to merge 2 commits into
cortexproject:masterfrom
AllenYTF:return-499-on-client-push-cancellation

Conversation

@AllenYTF

@AllenYTF AllenYTF commented Jul 23, 2026

Copy link
Copy Markdown

What this PR does

Maps client-canceled remote-write and OTLP push requests to HTTP 499 (Client Closed Request) instead of 500, so ordinary client-side cancellations (disconnects, or a fronting proxy/LB timeout) are no longer counted as server-side errors.

Fixes #7713.

Root cause

ring.DoBatch returns a bare context.Canceled (pkg/ring/batch.go, return ctx.Err()) when the client cancels the request context. Unlike the other error paths in the distributor's Push (which wrap via httpgrpc.Errorf), this error carries no gRPC status, so in the push HTTP handler httpgrpc.HTTPResponseFromError returns ok == false and the handler falls through to the generic http.StatusInternalServerError.

The query-frontend already handles this correctly (pkg/frontend/transport/handler.go, StatusClientClosedRequest = 499). This ports the same convention to the write path.

Changes

  • pkg/util/push/push.go: in the PRW1 and PRW2 handlers, map context.Canceled (via errors.Is, so wrapped cancellations are also caught) to httpgrpc.Errorf(util_api.StatusClientClosedRequest, ...) before status extraction, reusing the shared StatusClientClosedRequest = 499 constant. Excludes 499 from the "push refused" warn log.
  • pkg/util/push/otlp.go: same mapping for the OTLP write path.
  • Tests for bare and wrapped cancellation across the PRW1, PRW2, and OTLP handlers.
  • CHANGELOG entry.

Impact

Client-canceled push requests are currently logged and alerted on as 5xx errors, which can trigger false-positive SLO/error-rate alerts and pollute error dashboards. After this change they return 499.


🤖 AI assistance disclosure: This change was prepared with the help of Claude Code. The author has reviewed and understands every line and takes full responsibility per the project's GenAI policy.

Maps client-canceled remote-write push requests to HTTP 499 (Client
Closed Request) instead of 500, so ordinary client-side cancellations
(disconnects, or a fronting proxy/LB timeout) are no longer counted as
server-side errors.

ring.DoBatch returns a bare context.Canceled when the client cancels
the request context. Unlike the other error paths in the distributor's
Push (which wrap via httpgrpc.Errorf), this error carries no gRPC
status, so httpgrpc.HTTPResponseFromError returns ok == false and the
handler falls through to the generic 500. The query-frontend already
handles this correctly (StatusClientClosedRequest = 499); this ports
the same convention to the write path.

Changes:
- pkg/util/push/push.go: in the PRW1 and PRW2 handlers, map
  context.Canceled (via errors.Is, so wrapped cancellations are also
  caught) to httpgrpc.Errorf(util_api.StatusClientClosedRequest, ...)
  before status extraction, and exclude 499 from the push refused warn
  log.
- pkg/util/push/otlp.go: same mapping for the OTLP write path.
- Tests for bare and wrapped cancellation across the PRW1, PRW2, and
  OTLP handlers.

Signed-off-by: Allen Yan <allen_yan@apple.com>
Signed-off-by: Allen Yan <allen_yan@apple.com>
@AllenYTF
AllenYTF force-pushed the return-499-on-client-push-cancellation branch from 8b50c56 to c9509d8 Compare July 23, 2026 01:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Distributor push logs and metrics record 500 instead of 499 on client cancellation

2 participants