fix(terminal): don't close pane WebSocket mid-handshake on teardown - #117
Merged
Conversation
The terminal modal's cleanup called ws.close() unconditionally, including when the socket was still CONNECTING — which makes the browser log "WebSocket is closed before the connection is established". React StrictMode's mount→unmount→mount cycle hits this on every modal open in dev (it builds a throwaway socket and tears it down before it connects), and a genuine fast dismiss (close before connect) trips it in production too. Cleanup now detaches the socket's handlers and, if it's still CONNECTING, defers the close to onopen so it closes cleanly once connected; an already-open socket still closes immediately. Detaching handlers also prevents a late frame from writing to the just-disposed terminal or re-arming the reconnect logic. Adds the real readyState constants to the FakeWebSocket test double so WebSocket.CONNECTING/OPEN resolve under test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 25, 2026
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
Kills the dev-console warning
WebSocket is closed before the connection is establishedlogged fromTerminalModal.tsxon (in dev) every modal open.Cause
The terminal modal's cleanup called
ws.close()unconditionally — including when the socket was still inCONNECTING. Closing a socket mid-handshake is exactly what makes the browser emit that warning.React StrictMode (dev only) mounts every component twice — mount → unmount → remount — so opening a modal builds a throwaway WebSocket and tears it down before it finishes connecting, tripping the warning every time. A genuine fast dismiss (closing the modal before the socket connects) trips it in production too.
Fix
On teardown the cleanup now:
onmessage/onerror/onclosehandlers (so a late frame can't write to the just-disposed terminal or re-arm reconnect logic), andCONNECTING, defers the close toonopenso it closes cleanly once connected; an already-open socket still closes immediately.Tests (TDD)
TerminalModal — WebSocket teardowntests: aCONNECTINGsocket must not be closed directly on unmount (handlers detached, close deferred toonopen); anOPENsocket closes immediately. Watched the first fail against the old code, then pass.readyStateconstants (CONNECTING/OPEN/CLOSING/CLOSED) to theFakeWebSockettest double so the component'sWebSocket.CONNECTINGcheck resolves under test.Notes
Cannot read properties of undefined (reading 'dimensions')error — that's an xterm 5.3 internal render-after-dispose race, dev-only (gone in production builds), and can't be reproduced under test since xterm is mocked. Tracked separately.🤖 Generated with Claude Code