#13942: Fix: chrome-extension: make relay reconnect reliable with one tab
stale
Cluster:
Chrome Extension Enhancements
## Summary
- simplify browser extension recovery to a single reconnect candidate tab instead of per-tab queueing
- detach existing debugger sessions on relay close and reconnect only the most recently attached tab
- replace async interval retries with a serialized retry pass to avoid overlapping attach attempts and race conditions
## Test plan
[V] restart gateway and confirm tab transitions to connecting and auto-recovers back to ON
[V] attach extension to a tab and confirm badge shows ON
[V] close the candidate tab during reconnect window and confirm retries stop cleanly
[V] manual click attach/detach still works as expected
## Checklist
**[Title]** Mark as AI-assisted in the PR title or description
**[fully]** Note the degree of testing (untested / lightly tested / fully tested)
**[included planning and files changed]** Include prompts or session logs if possible (super helpful!)
**[I confirm]** Confirm you understand what the code does
# Session Plan
```
## Problem
When the OpenClaw Gateway restarts (browser relay), the Chrome extension loses connection and goes into a "connecting" state (yellow `…` badge). Currently requires manual clicking the extension icon on each tab to re-attach. This is annoying during development when the gateway restarts frequently.
## Conversation Context
**Date:** 2026-02-10
**User Question:** "Why not automate the relay attachment automatic?"
**Finding:** Manual attachment is intentional security design, but auto-reconnect after transient failures is reasonable.
From reviewing `background.js` and `extension-relay.ts`:
### What Happens on Restart
1. Gateway stops → WebSocket closes
2. `onRelayClosed()` fires → clears all tab attachments
3. Extension shows yellow `…` badge
4. **Gap:** No auto-retry for previously-attached tabs
### Why Not Fully Automatic (Security)
- `chrome.debugger.attach()` grants CDP access (network intercept, JS execution)
- Chrome requires user gesture per tab
- Extension doesn't know if YOU want that specific tab re-attached
**However:** Re-attaching previously-authorized tabs after transient disconnect is reasonable.
---
## Proposed Solution
Add persistent attachment tracking with auto-reconnect:
### Changes to `background.js`
1. **Track attachment history:**
// Store tab IDs that were attached before disconnect
const recentlyDetached = new Map<number, {targetId: string, retryCount: number}>()
2. **On disconnect, queue for retry:**
function onRelayClosed(reason) {
// ... existing cleanup ...
// Queue recently-attached tabs for auto-retry
for (const [tabId, tab] of tabs.entries()) {
if (tab.state === 'connected') {
recentlyDetached.set(tabId, { targetId: tab.targetId, retryCount: 0 })
}
}
// Start auto-reconnect loop
startAutoReconnect()
}
3. **Auto-reconnect loop:**
let autoReconnectTimer: number | null = null
function startAutoReconnect() {
if (autoReconnectTimer) return
autoReconnectTimer = setInterval(async () => {
// Check if relay is back
try {
await fetch(`http://127.0.0.1:${await getRelayPort()}/`,
{ method: 'HEAD', signal: AbortSignal.timeout(1000) })
} catch {
return // Relay still down
}
// Try re-attaching queued tabs
for (const [tabId, state] of recentlyDetached.entries()) {
if (state.retryCount > 3) {
recentlyDetached.delete(tabId) // Give up
continue
}
try {
await attachTab(tabId, { skipAttachedEvent: false })
recentlyDetached.delete(tabId) // Success
} catch {
state.retryCount++
}
}
if (recentlyDetached.size === 0) {
clearInterval(autoReconnectTimer!)
autoReconnectTimer = null
}
}, 2000) // Check every 2 seconds
}
### Optional: Per-Tab Setting
Add to `options.js`:
// Allow users to disable auto-reconnect
const settings = {
autoReconnect: true, // Default: enabled
maxRetries: 3,
retryDelayMs: 2000
}
---
## Implementation Notes
### Files to Modify
- `assets/chrome-extension/background.js` - Core logic
- `assets/chrome-extension/options.js` - Settings UI
- `assets/chrome-extension/options.html` - Toggle checkbox
### Security Considerations
- Only re-attach tabs that were previously attached in this session
- Don't auto-attach tabs that were never manually attached
- Respect Chrome's debugger permission model (no bypass)
- Clear retry queue on browser restart (don't persist)
### Edge Cases
- Tab closed while retry queued → skip gracefully
- Tab navigated to different URL → still re-attach (user intent)
- Multiple tabs attached → retry all
- Relay flapping (start/stop rapidly) → exponential backoff
---
## Tab Lifecycle & Authorization
**Key Principle:** Authorization is **per tab instance**, not per URL or browser session.
```
Most Similar PRs
#16743: fix: auto-reattach browser relay debugger after navigation
by jg-noncelogic · 2026-02-15
80.8%
#10390: fix(chrome-relay): sticky attach + auto-restore after disconnects
by Mokoby · 2026-02-06
79.2%
#17588: fix(relay): survive WS disconnects and MV3 worker restarts
by Unayung · 2026-02-15
78.4%
#19766: fix: Chrome relay extension auto-reattach after SPA navigation
by nishantkabra77 · 2026-02-18
76.2%
#15817: fix(chrome-relay): auto-reconnect, MV3 persistence, and keepalive
by derrickburns · 2026-02-13
75.7%
#20688: fix(browser): allow extension reconnect when stale websocket linger...
by HOYALIM · 2026-02-19
70.2%
#21314: feat: enhance browser relay with custom naming and diagnostic tools
by kelvinCB · 2026-02-19
68.9%
#22571: fix(browser): complete extension relay handshake on connect.challenge
by pandego · 2026-02-21
68.7%
#16060: fix(browser): require relay auth token for /extension WebSocket and...
by coygeek · 2026-02-14
66.6%
#22252: fix(chrome-extension): connection validation and clarify relay port...
by krizpoon · 2026-02-20
65.9%