fix(worker): interactive terminal sessions hang when the upgrade stalls - #129
Conversation
interactiveTerminalFetch issues the WebSocket upgrade handshake with no AbortSignal, so if the upstream never completes the 101 the awaited call never settles and terminal session setup wedges indefinitely. Bound it with the same 10s timeout runtimeAdapterFetch already uses two functions above in the same file. This bounds only the handshake: once the promise resolves with response.webSocket set, a later abort is a no-op against an already-fulfilled promise, so established sessions are unaffected.
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed September 8, 2026, 3:30 AM ET / 07:30 UTC. ClawSweeper reviewWhat this changesAdds a ten-second timeout to upstream terminal WebSocket requests, a signal-presence test, and an Unreleased changelog entry. Regression provenancePossible regression — suspected (reviewed change). No predecessor PR is attributed. Merge readiness⛔ Blocked before merge - 7 items remain The timeout is still needed, but the proposed implementation also aborts healthy terminal connections after ten seconds. The submitted tests do not exercise this runtime behavior. Priority: P2 Review scores
Verification
How this fits togetherCrabfleet’s Worker connects browser terminal subscriptions to runtime-provider WebSockets. This transport selects the coordinator binding or network fetch, then hands the upgraded socket to the terminal relay. flowchart LR
A[Browser terminal subscription] --> B[Terminal service]
B --> C[Select upstream transport]
C --> D[Coordinator binding]
C --> E[Network fetch]
D --> F[WebSocket handshake]
E --> F
F --> G[Terminal relay or connection error]
Before merge
Findings
Agent review detailsSecurityNone. Review metricsNone. Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Use an AbortController with a ten-second timer cleared in finally after awaiting the upgrade, preserving established sockets while bounding stalled handshakes. Do we have a high-confidence way to reproduce the issue? Yes, source establishes both the unbounded main-branch handshake and the patch’s cancellation of established sockets; neither scenario was executed during this read-only review. Is this the best way to solve the issue? No. AbortSignal.timeout remains effective after a Cloudflare WebSocket upgrade; a timer cleared when the awaited handshake settles is the narrow repair. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against bdd5083b0d3d. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
|
Same defect class as #123-#128, in a file those did not reach.
What Problem This Solves
Resolves a problem where starting an interactive terminal session can hang indefinitely.
interactiveTerminalFetchissues the WebSocket upgrade handshake with noAbortSignal, so when theupstream never completes the 101, the awaited call never settles and session setup wedges.
Why This Change Was Made
Bound the handshake with the same 10s timeout
runtimeAdapterFetchuses two functions above in the samefile. This bounds the upgrade only: once the promise resolves with
response.webSocketset, a laterabort is a no-op against an already-fulfilled promise, so established sessions are unaffected.
User Impact
A terminal session against an unresponsive runtime fails in about ten seconds instead of hanging until
the client gives up.
Evidence
AssertionError: The expression evaluated to a falsy value; 4 pass, 1 fail, nothing else in the file breaks.tests/runtime-adapter-transport.test.ts5/5.pnpm test: 1004 tests, 1000 pass, 4 fail. A clean checkout gives 1003 / 999 / 4 with an identicalfailing set, so this adds one passing test and no new failures.
The test asserts the signal as this file already does for
runtimeAdapterFetch, rather than waiting outa real abort.
Could not verify:
pnpm checkdoes not complete on my Windows machine, on this branch or on a cleancheckout.
scripts/generate-assets.mjsspawns vite as/C:/Users/.../vite/bin/vite.js, which Noderesolves to
C:\C:\Users\...and failsMODULE_NOT_FOUND. It reproduces without my change, so lint andformat are unproven from here.