Skip to content

fix(browser): handle stale extension WebSocket on reconnect#18698

Closed
codexGW wants to merge 1 commit intoopenclaw:mainfrom
codexGW:fix/extension-relay-stale-409
Closed

fix(browser): handle stale extension WebSocket on reconnect#18698
codexGW wants to merge 1 commit intoopenclaw:mainfrom
codexGW:fix/extension-relay-stale-409

Conversation

@codexGW
Copy link
Contributor

@codexGW codexGW commented Feb 17, 2026

Problem

The relay server rejects new extension connections with 409 Extension already connected when extensionWs is non-null, even if the underlying socket is already in CLOSING or CLOSED state.

This commonly happens with Manifest V3 service workers — Chrome kills the worker after ~30s of inactivity, and when it restarts and attempts to reconnect, the old socket is still lingering in a half-closed state. The user sees a connection failure and has to manually toggle the extension.

Fix

Two small, targeted changes to extension-relay.ts:

  1. Only reject with 409 when the existing socket is actually OPEN. If the socket is stale (CLOSING/CLOSED), terminate it and accept the new connection.

  2. Add an identity guard in the close handler so that a late-firing close event from a replaced socket does not wipe state belonging to the newer active connection.

What this does NOT change

  • No changes to the Chrome extension itself (background.js)
  • No new dependencies or permissions
  • Existing behavior when a live extension is connected is unchanged (still returns 409)

Testing

Tested by attaching the extension, restarting the gateway (simulating stale socket), and reconnecting — the extension now connects cleanly without requiring a manual toggle.

Greptile Summary

Fixes a reconnection issue in the Chrome extension relay where stale WebSocket connections (in CLOSING/CLOSED state) prevented new extension connections from being established. The fix checks socket state before rejecting with 409, terminates stale sockets, and adds an identity guard in the close handler to prevent late-firing close events from wiping state of newer connections. This resolves the common Manifest V3 service worker scenario where Chrome kills the worker after ~30s inactivity, leaving lingering half-closed sockets.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are surgical and well-targeted: they add defensive checks for socket state and identity without altering the core connection logic. The fix correctly handles the stale socket race condition by checking readyState before rejection and preventing old close handlers from interfering with new connections via identity comparison. Both changes align with WebSocket best practices.
  • No files require special attention

Last reviewed commit: 21c2e68

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

@codexGW
Copy link
Contributor Author

codexGW commented Feb 17, 2026

Note: PR #15817 also touches this 409 path, but takes a different approach — it unconditionally replaces any existing extension socket (even if OPEN) and does not add a close-handler identity guard.

This PR is intentionally more conservative:

  • Only replaces sockets that are actually stale (CLOSING/CLOSED), preserving the 409 rejection for genuinely concurrent connections.
  • Adds the identity guard in the close handler to prevent the old socket's late close event from wiping the new connection's state.

Both PRs are independent and non-conflicting in intent, but would need a merge conflict resolution if both land.

The relay server rejects new extension connections with 409 when
extensionWs is non-null, even if the underlying socket is already
in CLOSING or CLOSED state. This commonly happens when a Manifest V3
service worker is killed and restarts — the old socket lingers in a
stale state and blocks the fresh connection attempt.

Two targeted fixes:

1. Only reject with 409 when the existing socket is actually OPEN.
   If it is stale (CLOSING/CLOSED), terminate it and accept the
   new connection.

2. Add an identity guard in the close handler so that a late-firing
   close event from a replaced socket does not wipe state belonging
   to the active connection.
@sebslight
Copy link
Member

Closing as duplicate of #20688. If this is incorrect, please contact us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants