fix(proxy): settle the pending read when an upstream socket closes - #4158
Conversation
UpstreamWebSocket#pump awaits reader.read() for the next frame. Closing the stream resolves `closed` and fires onclose, but it does not settle a read that is already awaiting, so the operation outlived the connection it belonged to. In the proxy this leaks one pending read per closed socket. In CI it surfaces as an intermittent "Leaks detected: an async operation to receive the next message on a WebSocket was started in this test, but never completed" from src/proxy/websocket-client.test.ts under --trace-leaks --parallel. It has failed a coverage shard on pull requests that touch neither the proxy nor WebSockets. Hold the reader and cancel it in #close, which settles the pending read and lets #pump unwind and release its lock. Claude-Session: https://claude.ai/code/session_01TNbcqUy64goaeCShfjbRmf
There was a problem hiding this comment.
kojiwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
You have reached your Codex usage limits for security reviews. Please try again later. |
|
Warning Review limit reachedNext included review available in 36 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 Client bundle boundary
A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in |
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@codex review Please review the exact current head . All existing review threads are resolved. Please report any remaining findings against this SHA. |
1 similar comment
|
@codex review Please review the exact current head . All existing review threads are resolved. Please report any remaining findings against this SHA. |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |



Description
UpstreamWebSocket#pumpawaitsreader.read()for the next frame:Closing the stream resolves
closedand firesonclose, but it does not settle aread()that is already awaiting. The read outlives the connection it belongs to.In the proxy that leaks one pending read per closed socket. In CI it surfaces as an intermittent failure from
src/proxy/websocket-client.test.tsunder--trace-leaks --parallel:Why this is worth fixing now rather than filing
It is not confined to the proxy. This exact failure took out
coverage shard 8/8on two separate pull requests that touch neither the proxy nor WebSockets — one changing onlysrc/transforms/import-rewriter/, one changing onlysrc/modules/server/. Every occurrence costs a shard re-run and, worse, invites the reading that the shard is "just flaky" when a real regression eventually lands there.Fix
Hold the reader and cancel it in
#close. Cancelling settles the pending read,#pumpunwinds, andreleaseLockruns. The#settledguard already prevents re-entry from the#failpath.Type of Change
Checklist
Testing
Chasing the flake by re-running was not going to prove anything, so the regression guard is deterministic instead. It uses the client's existing
UpstreamWebSocketStreamFactoryseam with a stream whose readable never produces a frame and whoseclose()only resolvesclosed, which is precisely what a realWebSocketStreamdoes. The test then asserts the read was settled.Verified to fail without the fix:
Found while shepherding the issue-triage PRs (#4147, #4148, #4149, #4150, #4152, #4154), where it twice failed a shard on unrelated changes.