Repository navigation
fix(shared): give each API client its own HTTP transport - #497
Merged
Merged
Conversation
client.New used http.DefaultTransport. httptest.Server.Close calls CloseIdleConnections on http.DefaultTransport, so any parallel test that shut down its server could close another test's connection. net/http returns a connection to the idle pool before it hands a bodiless response (such as a 204) to the waiting request; closing it in that window fails the request with "HTTP/1.x transport connection broken: http: CloseIdleConnections called". That is the intermittent failure in the jtk delete, remote link, field, field context and sprint tests. Each client now uses a clone of http.DefaultTransport, so other code closing the default pool's idle connections cannot affect it. The delete tests now print the per-issue errors from stderr when runDelete fails, since its returned error only says "already reported".
monit-reviewer
approved these changes
Sep 30, 2026
monit-reviewer
left a comment
There was a problem hiding this comment.
Automated PR Review
Reviewed commit: 7eafdb1e9a35
Profile: claude-monit-reviewer - Posting as: monit-reviewer
Summary
| Reviewer | Findings |
|---|---|
| go:implementation-tests | 0 |
| architecture:solid-reviewer-agnostic | 0 |
Reviewer Coverage
go:implementation-tests— complete (broad); skipped: none; constraints: nonearchitecture:solid-reviewer-agnostic— complete (broad); inspected 2 assigned files (3 inspected across reviewers):shared/client/client.go,shared/client/client_test.go; skipped: none; constraints: Only four production call sites construct client.New (jtk and cfl api packages), each once per command, so a per-Client transport does not multiply connection pools in practice. Ran go test ./client/ in shared (passes); did not run the full jtk/cfl suites or the PR's reproduction rig.
Inspected files (3)
shared/client/client.goshared/client/client_test.gotools/jtk/internal/cmd/issues/delete_test.go
0 PR discussion threads considered. 0 summarized; 0 resolved.
Completed in 1m 47s | ~$1.16 (est.) | claude-sonnet-5, claude-opus-5-5 | cr 0.10.318
| Field | Value |
|---|---|
| Model | claude-sonnet-5, claude-opus-5-5 |
| Reviewers | go:implementation-tests, architecture:solid-reviewer-agnostic |
| Engine | claude_cli · claude-sonnet-5, claude-opus-5-5 |
| Reviewed by | cr · monit-reviewer |
| Duration | 1m 47s wall · 2m 01s compute |
| Cost | ~$1.16 (est.) |
| Pricing basis | anthropic-public-2026-09-23 |
| Tokens | 36 in / 6.2k out |
Per-workstream usage
orchestrator-selection— claude-sonnet-5- In: 6
- Out: 2.2k
- Cache read: 89.9k
- Cache create: 43.2k
- Cost: ~$0.21 (est.)
- Duration: 31s
go:implementation-tests— claude-sonnet-5- In: 12
- Out: 1.7k
- Cache read: 273.9k
- Cache create: 43.4k
- Cost: ~$0.25 (est.)
- Duration: 44s
architecture:solid-reviewer-agnostic— claude-opus-5-5- In: 12
- Out: 1.8k
- Cache read: 217.7k
- Cache create: 56.7k
- Cost: ~$0.53 (est.)
- Duration: 31s
orchestrator-rollup— claude-sonnet-5- In: 6
- Out: 378
- Cache read: 144.3k
- Cache create: 33.8k
- Cost: ~$0.17 (est.)
- Duration: 14s
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the intermittent
HTTP/1.x transport connection broken: http: CloseIdleConnections calledfailures in jtk tests. Seen on main inTestRunDelete_MultipleIssues,TestRunDelete(remote links),TestRunDelete_Force(fields),TestDeleteFieldContextandTestRunRemove_IDOnly(sprints).Root cause:
client.Newbuilt anhttp.Clientwith noTransport, so every client sharedhttp.DefaultTransport.httptest.Server.Closecallshttp.DefaultTransport.CloseIdleConnections()on every test server shutdown. For a response without a body (a 204, or an empty 200), net/http puts the connection back in the idle pool inreadLoopbefore it hands the response to the waitingroundTrip. If another parallel test closes its server in that window, the connection is closed witherrCloseIdleConns,roundTripsees the close before the response, and the request fails. Every failing test uses a bodiless response (DELETE 204, field trash, backlog move).Changes:
shared/client: eachClientgets its ownhttp.DefaultTransport.Clone(), so other code closing the default pool cannot touch its connections. The clone keeps the default proxy, dial, TLS and HTTP/2 settings. This covers jtk and cfl, which both build on this client.tools/jtk/internal/cmd/issues/delete_test.go: whenrunDeletefails, the test prints the per-issue errors it wrote to stderr. The returned error is onlyalready reported (1 of 3 issue(s) failed), which hid the transport error in CI.Tests
Reproduction: the flake did not reproduce at package level locally (
issues.test -test.count=200 -test.cpu 1,2,8: 0 failures in 600 package runs), so I reproduced the mechanism directly with a temporary rig inshared/client(source below, not committed): 4 goroutines each issue DELETEs returning 204 against their ownhttptestserver throughclient.New, while another goroutine callshttp.DefaultTransport.(*http.Transport).CloseIdleConnections()every 2ms, the same callhttptest.Server.Closemakes. Command, 15s per run:Every failure before was the exact CI error:
request failed: Delete "http://127.0.0.1:<port>/x": net/http: HTTP/1.x transport connection broken: http: CloseIdleConnections called.TestNew/owns_its_transportasserts clients do not sharehttp.DefaultTransportor each other's transport.go test -race ./...andgolangci-lint run ./...pass inshared,tools/jtkandtools/cfl.Reproduction rig