go-httptest-sse-deferred-close-client-timeout-flake
The solution is written to ~/solution.md and verified against a working reproduction in ~/sseflake/.
Root cause chain
http.Client.Timeout (30s) is a deadline for headers and the response body, so a long-lived SSE read stays governed by it. CloseIdleConnections() can't cancel an active request.httptest.Server.Close() blocks on all outstanding handlers and deliberately won't close StateActive connections — so an in-flight SSE handler pins it.defer stub.Close() runs at function return, while t.Cleanup(svc.Close) runs after defers → the stub was closed while the service still held the stream open. Only the 30s client timeout unblocked it.r.Context() and had no write deadline, extending the hang.Fix (4 parts)
context, requests use http.NewRequestWithContext, and Close() cancels it + waits for the reader goroutine.r.Context().Done() and sets per-write deadlines via http.ResponseController.SetWriteDeadline.CloseClientConnections() then Close() with a 5s timeout guard.t.Cleanup: register stub cleanup first, service cleanup second.Verified results (Go 1.26, Linux)
| Test | Time |
|---|---|
| Buggy path (2s scaled timeout) | 2.04s — pinned to client timeout |
| Fixed path | 210µs |
| Stress, delayed handler (30s client timeout) | 91µs |
The bounded CloseStub also independently rescues a service that never cancels (~60ms measured), because CloseClientConnections() closes the active socket and cancels the handler context.
# Evidence - Problem class: go-httptest-sse-deferred-close-client-timeout-flake - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-21T01:47:53.327Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Test-only CI flake where teardown waits ~30s: the SSE client default http.Client.Timeout (30s) covers the body read, and the httptest stub was closed via defer BEFORE the service Close (t.Cleanup LIFO runs it after) \u2014 teardown waits on the in-flight SSE handler up to the client timeout under CI load. Fix: make the stub handler exit on request-context cancellation, add write deadlines, bound the stub server Close (CloseClientConnections + bounded wait), and order stub cleanup AFTER Service.Close. Add a stress test with a delayed SSE handler asserting Close finishes well under the timeout.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-httptest-sse-deferred-close-client-timeout-flake", "provider": "openrouter", "solved_at": "2026-09-21T01:47:53.327Z", "version": ""}The solution is written to ~/solution.md and verified against a working reproduction in ~/sseflake/.
Root cause chain
http.Client.Timeout (30s) is a deadline for headers and the response body, so a long-lived SSE read stays governed by it. CloseIdleConnections() can't cancel an active request.httptest.Server.Close() blocks on all outstanding handlers and deliberately won't close StateActive connections — so an in-flight SSE handler pins it.defer stub.Close() runs at function return, while t.Cleanup(svc.Close) runs after defers → the stub was closed while the service still held the stream open. Only the 30s client timeout unblocked it.r.Context() and had no write deadline, extending the hang.Fix (4 parts)
context, requests use http.NewRequestWithContext, and Close() cancels it + waits for the reader goroutine.r.Context().Done() and sets per-write deadlines via http.ResponseController.SetWriteDeadline.CloseClientConnections() then Close() with a 5s timeout guard.t.Cleanup: register stub cleanup first, service cleanup second.Verified results (Go 1.26, Linux)
| Test | Time |
|---|---|
| Buggy path (2s scaled timeout) | 2.04s — pinned to client timeout |
| Fixed path | 210µs |
| Stress, delayed handler (30s client timeout) | 91µs |
The bounded CloseStub also independently rescues a service that never cancels (~60ms measured), because CloseClientConnections() closes the active socket and cancels the handler context.
# Evidence - Problem class: go-httptest-sse-deferred-close-client-timeout-flake - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-21T01:47:53.327Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Test-only CI flake where teardown waits ~30s: the SSE client default http.Client.Timeout (30s) covers the body read, and the httptest stub was closed via defer BEFORE the service Close (t.Cleanup LIFO runs it after) \u2014 teardown waits on the in-flight SSE handler up to the client timeout under CI load. Fix: make the stub handler exit on request-context cancellation, add write deadlines, bound the stub server Close (CloseClientConnections + bounded wait), and order stub cleanup AFTER Service.Close. Add a stress test with a delayed SSE handler asserting Close finishes well under the timeout.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-httptest-sse-deferred-close-client-timeout-flake", "provider": "openrouter", "solved_at": "2026-09-21T01:47:53.327Z", "version": ""}