Skip to content

fix: do not return 502 on runner live-log client disconnect#4900

Closed
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/agent-d65a432c
Closed

fix: do not return 502 on runner live-log client disconnect#4900
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/agent-d65a432c

Conversation

@cursor
Copy link
Copy Markdown
Contributor

@cursor cursor Bot commented May 20, 2026

Problem

Sentry surfaced a noisy info-level event for the runner live-log streaming endpoint:

HTTP 502 /api/v1/canvases/{canvas_id}/node-executions/{execution_id}/runner-live-logs
Sentry issue 7494071660

handleRunnerLiveLogStream in pkg/public/runner_live_log_stream.go is a streaming proxy in front of the task-broker live-log endpoint. It returned 502 Bad Gateway whenever http.Client.Do(req) failed.

For a streaming endpoint, by far the most common reason for that call to fail is the client aborting the request — for example, the user closing the live-log dialog or navigating away from the page. The frontend (web_src/src/ui/CanvasPage/RunnerLiveLogDialog/liveLogStream.ts) explicitly does this via AbortController.abort(). When that happens, the request context is cancelled and Do returns a context.Canceled error.

The logging middleware (pkg/public/middleware/logging.go) then captured those 502s as Sentry events (the shouldCaptureHTTPError predicate captures every 5xx except 501 and 505), generating noise that does not reflect a real server bug.

Fix

Detect client disconnects in the handler and respond with 499 Client Closed Request (nginx convention) instead of 502 Bad Gateway. Since 499 < 500, the Sentry-capture middleware ignores it — but real upstream connectivity failures still return 502 and remain visible in Sentry.

Implementation:

  • Add a small isClientDisconnect(ctx, err) helper that checks both ctx.Err() and the error chain (context.Canceled / context.DeadlineExceeded).
  • Use it in both the http.NewRequestWithContext and http.DefaultClient.Do error paths.
  • Introduce a named statusClientClosedRequest = 499 constant with a comment explaining the rationale (so the magic number is self-documenting).

Tests

Added a new subtest TestHandleRunnerLiveLogStream/client_disconnect_does_not_return_5xx that points the handler at an upstream httptest.Server which blocks on the request context, then cancels the request context from the client side. It asserts the response is 499, not 502.

make test PKG_TEST_PACKAGES=./pkg/public/
✓  pkg/public (10.514s)
DONE 121 tests in 10.514s

Also verified:

  • make format.go — clean
  • make lint — clean
  • make check.build.app — succeeds

Risk

Very low. The behavior change is scoped to a single streaming handler, and the new branch is only taken when the request context has already been cancelled (i.e. the client is no longer listening to the response anyway). Genuine upstream errors continue to return 502 as before.

Open in Web Open in Cursor 

The runner live-log streaming endpoint proxies an upstream task-broker
HTTP request. When the client (browser) aborts the in-flight fetch
(e.g. closes the live-log dialog or navigates away), the request
context is cancelled and http.Client.Do returns a context error. The
handler was treating that as an upstream failure and responding with
502 Bad Gateway, which the logging middleware then captured as a
Sentry event.

Detect cancellations via the request context / error chain and respond
with the non-5xx 499 status instead, matching the nginx convention for
client-closed requests. This keeps genuine upstream connectivity
failures returning 502 (and visible in Sentry) while suppressing the
expected client-cancel noise for this streaming endpoint.

Co-authored-by: Aleksandar Mitrovic <AleksandarCole@users.noreply.github.com>
@superplanehq-integration
Copy link
Copy Markdown

👋 Commands for maintainers:

  • /sp start - Start an ephemeral machine (takes ~30s)
  • /sp stop - Stop a running machine (auto-executed on pr close)

@forestileao forestileao marked this pull request as ready for review May 20, 2026 19:22
@forestileao forestileao self-assigned this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants