Dashboard Console WebSocket Reconnection Issue Analysis
Analysis of mobile reconnection failures vs admin broadcast reliability
Output
🔌 Dashboard Console WebSocket Reconnection Issue
Problem Summary
When mobile browser pauses connections (screen off, app switch), the console WebSocket fails to properly reconnect, while the admin broadcast WebSocket recovers instantly.
Symptoms:
- Status bar shows "Reconnecting" indefinitely
- Manual refresh shows "shadow" console - no live updates
- Assistant text never appears
- Admin broadcast WebSocket (navbar indicator) reconnects instantly ✓
Root Cause Analysis
Admin Broadcast (works): /api/admin/ws
- Simple WebSocket that only receives broadcast events
- Client reconnection in
layout.ts:746-790 - Visibility change handler at line 1231-1237
- Exponential backoff with 1s reset on visibility
Console WebSocket (broken): /api/console/ws
- More complex - handles bi-directional messaging (prompts, events, turns)
- Client reconnection in
console-websocket-client.ts:115-145 - Visibility change handler at lines 290-298
- 🔴 ISSUE: Recovery state machine not properly notified when WebSocket reconnects
Key Differences Found
| Aspect | Admin Broadcast | Console WS |
|---|---|---|
| Visibility Handler | Resets backoff to 1s | Resets attempts to 0 |
| On Reconnect | Simple welcome msg | Sends welcome, but no state sync |
| State Recovery | N/A (stateless) | Needs to resume/recover turn state |
| Status Updates | N/A | Status bar stuck on "Reconnecting" |
Specific Issues Identified
1. onopen handler doesn't notify state machine
In console-websocket-client.ts:67-79, when WebSocket reconnects it updates the status dot/label but doesn't dispatch a state machine event to resume the turn.
2. No active turn sync on reconnect
When reconnecting, the server knows if a turn is active (client.currentRequestId) but doesn't send a turn_start message to re-sync the client.
3. attemptRecovery guards prevent re-recovery
In console-recovery.ts:153-156, if state.recoveryComplete is true, recovery is skipped. But after a reconnect, we may need to re-trigger recovery.
4. Status bar not updated post-reconnect
The visibility change handler dispatches page_visible event but the state machine doesn't handle "reconnected and turn in progress" state properly.
Proposed Fixes
- Add reconnect state sync to server:
console-websocket.ts- On WebSocket open, if client has active turn, sendturn_start+ buffered events - Dispatch state machine event on reconnect:
console-websocket-client.ts:onopen- Adddispatch({ type: 'reconnect' })after status update - Handle reconnect in state machine:
console-sm-transitions.ts- Add reconnect handler that checks for pending state and resumes turn - Reset recovery flags on reconnect:
console-recovery.ts- Allow re-recovery when WebSocket reconnects during active session
What's Next?
Choose how to proceed:
Quick Actions
Original Request
No layout configured
Details
Raw Data
{
"id": "b1b4fee3-8159-4a93-b44b-cf75001423a8",
"type": "general",
"status": "completed",
"title": "Dashboard Console WebSocket Reconnection Issue Analysis",
"description": "Analysis of mobile reconnection failures vs admin broadcast reliability",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 16px 0;\">🔌 Dashboard Console WebSocket Reconnection Issue</h2>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#f59e0b;margin:0 0 12px 0;\">Problem Summary</h3>\n<p style=\"margin:0 0 8px 0;\">When mobile browser pauses connections (screen off, app switch), the console WebSocket fails to properly reconnect, while the admin broadcast WebSocket recovers instantly.</p>\n<p style=\"margin:0;\"><strong>Symptoms:</strong></p>\n<ul style=\"margin:8px 0;padding-left:20px;color:#9ca3af;\">\n<li>Status bar shows \"Reconnecting\" indefinitely</li>\n<li>Manual refresh shows \"shadow\" console - no live updates</li>\n<li>Assistant text never appears</li>\n<li>Admin broadcast WebSocket (navbar indicator) reconnects instantly ✓</li>\n</ul>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#22c55e;margin:0 0 12px 0;\">Root Cause Analysis</h3>\n\n<p style=\"margin:0 0 8px 0;\"><strong>Admin Broadcast (works):</strong> <code style=\"color:#98c379;\">/api/admin/ws</code></p>\n<ul style=\"margin:8px 0;padding-left:20px;color:#9ca3af;\">\n<li>Simple WebSocket that only receives broadcast events</li>\n<li>Client reconnection in <code style=\"color:#e5c07b;\">layout.ts:746-790</code></li>\n<li>Visibility change handler at line 1231-1237</li>\n<li>Exponential backoff with 1s reset on visibility</li>\n</ul>\n\n<p style=\"margin:12px 0 8px 0;\"><strong>Console WebSocket (broken):</strong> <code style=\"color:#98c379;\">/api/console/ws</code></p>\n<ul style=\"margin:8px 0;padding-left:20px;color:#9ca3af;\">\n<li>More complex - handles bi-directional messaging (prompts, events, turns)</li>\n<li>Client reconnection in <code style=\"color:#e5c07b;\">console-websocket-client.ts:115-145</code></li>\n<li>Visibility change handler at lines 290-298</li>\n<li><span style=\"color:#ef4444;\">🔴 ISSUE:</span> Recovery state machine not properly notified when WebSocket reconnects</li>\n</ul>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#3b82f6;margin:0 0 12px 0;\">Key Differences Found</h3>\n\n<table style=\"width:100%;border-collapse:collapse;font-size:0.9rem;\">\n<tr style=\"background:#1e1e1e;\">\n<th style=\"text-align:left;padding:8px;border-bottom:1px solid #444;\">Aspect</th>\n<th style=\"text-align:left;padding:8px;border-bottom:1px solid #444;\">Admin Broadcast</th>\n<th style=\"text-align:left;padding:8px;border-bottom:1px solid #444;\">Console WS</th>\n</tr>\n<tr>\n<td style=\"padding:8px;border-bottom:1px solid #333;\">Visibility Handler</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#22c55e;\">Resets backoff to 1s</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#22c55e;\">Resets attempts to 0</td>\n</tr>\n<tr>\n<td style=\"padding:8px;border-bottom:1px solid #333;\">On Reconnect</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#22c55e;\">Simple welcome msg</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#ef4444;\">Sends welcome, but no state sync</td>\n</tr>\n<tr>\n<td style=\"padding:8px;border-bottom:1px solid #333;\">State Recovery</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#9ca3af;\">N/A (stateless)</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#ef4444;\">Needs to resume/recover turn state</td>\n</tr>\n<tr>\n<td style=\"padding:8px;border-bottom:1px solid #333;\">Status Updates</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#9ca3af;\">N/A</td>\n<td style=\"padding:8px;border-bottom:1px solid #333;color:#ef4444;\">Status bar stuck on \"Reconnecting\"</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#8b5cf6;margin:0 0 12px 0;\">Specific Issues Identified</h3>\n\n<p style=\"margin:0 0 8px 0;\"><strong>1. onopen handler doesn't notify state machine</strong></p>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;\">In <code>console-websocket-client.ts:67-79</code>, when WebSocket reconnects it updates the status dot/label but doesn't dispatch a state machine event to resume the turn.</p>\n\n<p style=\"margin:0 0 8px 0;\"><strong>2. No active turn sync on reconnect</strong></p>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;\">When reconnecting, the server knows if a turn is active (<code>client.currentRequestId</code>) but doesn't send a <code>turn_start</code> message to re-sync the client.</p>\n\n<p style=\"margin:0 0 8px 0;\"><strong>3. attemptRecovery guards prevent re-recovery</strong></p>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;\">In <code>console-recovery.ts:153-156</code>, if <code>state.recoveryComplete</code> is true, recovery is skipped. But after a reconnect, we may need to re-trigger recovery.</p>\n\n<p style=\"margin:0 0 8px 0;\"><strong>4. Status bar not updated post-reconnect</strong></p>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;\">The visibility change handler dispatches <code>page_visible</code> event but the state machine doesn't handle \"reconnected and turn in progress\" state properly.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#f59e0b;margin:0 0 12px 0;\">Proposed Fixes</h3>\n\n<ol style=\"margin:0;padding-left:20px;line-height:2;\">\n<li><strong>Add reconnect state sync to server:</strong><br>\n<code style=\"color:#98c379;\">console-websocket.ts</code> - On WebSocket open, if client has active turn, send <code>turn_start</code> + buffered events</li>\n\n<li><strong>Dispatch state machine event on reconnect:</strong><br>\n<code style=\"color:#98c379;\">console-websocket-client.ts:onopen</code> - Add <code>dispatch({ type: 'reconnect' })</code> after status update</li>\n\n<li><strong>Handle reconnect in state machine:</strong><br>\n<code style=\"color:#98c379;\">console-sm-transitions.ts</code> - Add reconnect handler that checks for pending state and resumes turn</li>\n\n<li><strong>Reset recovery flags on reconnect:</strong><br>\n<code style=\"color:#98c379;\">console-recovery.ts</code> - Allow re-recovery when WebSocket reconnects during active session</li>\n</ol>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;\">What's Next?</h4>\n<p style=\"color:#9ca3af;margin:0;\">Choose how to proceed:</p>\n</div>\n</div>",
"requestedAt": "2026-01-08T07:30:00Z",
"requestId": "58e88da5-cea2-4977-9acb-8b90fbd936d4",
"choices": [
{
"label": "Implement Fix",
"value": "Implement the WebSocket reconnection fixes identified in the analysis. Start with adding state sync on reconnect to console-websocket.ts",
"primary": true
},
{
"label": "Debug Session",
"value": "Set up a debug session to capture logs during mobile reconnection failure - add more verbose logging first"
},
{
"label": "Quick Workaround",
"value": "Implement a quick workaround - force page reload when WebSocket reconnects after extended disconnect"
}
],
"turnTiming": {
"totalMs": 110983,
"ttfeMs": 221,
"thinkingMs": 110760,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-08T07:30:52.973Z",
"updatedAt": "2026-01-08T07:31:01.259Z",
"requestId": "58e88da5-cea2-4977-9acb-8b90fbd936d4",
"scope": "vibetools",
"tags": [
"console",
"websocket",
"mobile",
"reconnection",
"bug"
],
"targetUser": "claude"
}