Skip to content

Recovery middleware catches http.ErrAbortHandler, corrupting proxy responses #4064

@gkatz2

Description

@gkatz2

Bug Description

The recovery middleware (pkg/recovery/recovery.go) catches all panics
indiscriminately, including http.ErrAbortHandler. This is a sentinel panic
value that Go's httputil.ReverseProxy uses intentionally when a streaming
response breaks mid-copy (reverseproxy.go:612). Go's HTTP server has built-in
handling for this: catch it silently, close the connection, move on.

By intercepting ErrAbortHandler, the recovery middleware:

  1. Logs a noisy stack trace for something that isn't a bug
  2. Tries to write http.Error(w, "Internal Server Error", 500) to an
    already-in-flight response, corrupting it
  3. Prevents Go's HTTP server from cleanly aborting the connection

Steps to Reproduce

  1. Run any MCP server through ToolHive's transparent proxy
  2. Have a client disconnect or timeout mid-stream (e.g., Claude Code closing
    an SSE connection)
  3. Observe Panic recovered: net/http: abort Handler ERROR log entries with
    full stack traces originating from reverseproxy.go:612

Expected Behavior

http.ErrAbortHandler should propagate to Go's HTTP server, which handles it
silently by closing the connection. No log output, no attempted 500 response.

Actual Behavior

Every aborted streaming response produces an ERROR-level log entry with a full
stack trace, and the middleware attempts to write a 500 response to a connection
that is already mid-response.

Environment

  • ToolHive v0.11.1 (also reproducible on current main)
  • OS: macOS (the code path is platform-independent, so likely affects Linux too)
  • Affects all servers using the transparent proxy

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggoPull requests that update go codeproxy

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions