Console Disconnect Bug Analysis
Analysis of the disconnect after timing bar issue
Output
🔍 Console Disconnect Bug Analysis
Problem
After a turn completes successfully (timing bar appears showing ~81.7s), the status bar shows "RECONNECTING..." and stays stuck. The server needs to be restarted to recover.
Your Observation
This only happens when the timing bar appears - i.e., on turns that run long enough to generate timing data. Short turns don't exhibit this.
Root Cause Hypothesis
There's a race condition in the stream completion flow:
- POST stream receives
kontextevent → timing bar displayed - POST stream receives
doneevent - Stream ends →
reader.read()returnsdone: true - Code calls
checkForNewKontask()→ succeeds - Dispatches
stream_end { hadOutput: true }→ should go to idle - ❌ BUT: The stream closing also triggers an error/disconnect event (browser behavior)
- Error handler calls
reconnectWithEventSource()→ stuck reconnecting
Why Only Long Turns?
Short turns finish so quickly that all events (including kontext and done) arrive in the same chunk. The stream closes cleanly before any async race can occur. Long turns allow time for async operations to overlap.
Proposed Fix
In console-streaming.ts, the catch block should check if the stream already completed successfully:
} catch (e) {
// Only reconnect if stream didn't complete normally
if (!streamComplete) {
reconnectWithEventSource(requestId, token);
}
}
The streamComplete flag is already set to true at line 279, but the catch block at line 307 doesn't check it.
Key Code Locations
| Stream handler | konui/src/console-streaming.ts:267-317 |
| reconnectWithEventSource | konui/src/console-streaming.ts:329-437 |
| State transition | konui/src/console-sm-transitions.ts:144-164 |
What's Next?
Choose an action:
Quick Actions
Original Request
No layout configured
Details
Raw Data
{
"id": "9d0fefd0-a92a-4392-8bb2-732f308df367",
"type": "general",
"status": "completed",
"title": "Console Disconnect Bug Analysis",
"description": "Analysis of the disconnect after timing bar issue",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:sans-serif;\">\n<h2 style=\"color:#fff;margin:0 0 16px 0;\">🔍 Console Disconnect Bug Analysis</h2>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#f87171;margin:0 0 12px 0;\">Problem</h3>\n<p style=\"color:#e0e0e0;margin:0;\">After a turn completes successfully (timing bar appears showing ~81.7s), the status bar shows \"RECONNECTING...\" and stays stuck. The server needs to be restarted to recover.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;\">Your Observation</h3>\n<p style=\"color:#e0e0e0;margin:0;\">This <strong>only happens when the timing bar appears</strong> - i.e., on turns that run long enough to generate timing data. Short turns don't exhibit this.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#98c379;margin:0 0 12px 0;\">Root Cause Hypothesis</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">There's a <strong>race condition</strong> in the stream completion flow:</p>\n<ol style=\"margin:0;padding-left:20px;color:#9ca3af;line-height:1.8;\">\n<li>POST stream receives <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">kontext</code> event → timing bar displayed</li>\n<li>POST stream receives <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">done</code> event</li>\n<li>Stream ends → <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">reader.read()</code> returns <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">done: true</code></li>\n<li>Code calls <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">checkForNewKontask()</code> → succeeds</li>\n<li>Dispatches <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">stream_end { hadOutput: true }</code> → should go to idle</li>\n<li>❌ <strong>BUT</strong>: The stream closing also triggers an error/disconnect event (browser behavior)</li>\n<li>Error handler calls <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">reconnectWithEventSource()</code> → stuck reconnecting</li>\n</ol>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#61afef;margin:0 0 12px 0;\">Why Only Long Turns?</h3>\n<p style=\"color:#e0e0e0;margin:0;\">Short turns finish so quickly that all events (including <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">kontext</code> and <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">done</code>) arrive in the same chunk. The stream closes cleanly before any async race can occur. Long turns allow time for async operations to overlap.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#c678dd;margin:0 0 12px 0;\">Proposed Fix</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">In <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">console-streaming.ts</code>, the catch block should check if the stream already completed successfully:</p>\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;color:#98c379;font-size:0.85em;margin:8px 0;\"><code>} catch (e) {\n // Only reconnect if stream didn't complete normally\n if (!streamComplete) {\n reconnectWithEventSource(requestId, token);\n }\n}</code></pre>\n<p style=\"color:#9ca3af;margin:12px 0 0 0;\">The <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">streamComplete</code> flag is already set to <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">true</code> at line 279, but the catch block at line 307 doesn't check it.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\">\n<h4 style=\"color:#fff;margin:0 0 8px 0;\">Key Code Locations</h4>\n<table style=\"width:100%;border-collapse:collapse;font-size:0.9em;\">\n<tr><td style=\"color:#7f848e;padding:4px 12px 4px 0;\">Stream handler</td><td style=\"color:#61afef;\">konui/src/console-streaming.ts:267-317</td></tr>\n<tr><td style=\"color:#7f848e;padding:4px 12px 4px 0;\">reconnectWithEventSource</td><td style=\"color:#61afef;\">konui/src/console-streaming.ts:329-437</td></tr>\n<tr><td style=\"color:#7f848e;padding:4px 12px 4px 0;\">State transition</td><td style=\"color:#61afef;\">konui/src/console-sm-transitions.ts:144-164</td></tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-top:16px;\">\n<h4 style=\"color:#fff;margin:0 0 8px 0;\">What's Next?</h4>\n<p style=\"color:#9ca3af;margin:0;\">Choose an action:</p>\n</div>\n</div>",
"requestedAt": "2026-01-06T01:15:00.000Z",
"requestId": "47d47151-9806-42de-a08d-bcf155bd913b",
"choices": [
{
"label": "Fix it now",
"value": "Apply the fix to console-streaming.ts to prevent reconnect after successful stream completion",
"primary": true
},
{
"label": "Add logging first",
"value": "Add more logging to capture the exact sequence of events before fixing"
},
{
"label": "Later",
"value": "Add this to the backlog for later"
}
],
"turnTiming": {
"totalMs": 125407,
"ttfeMs": 91,
"thinkingMs": 125316,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-05T16:42:35.787Z",
"updatedAt": "2026-01-05T16:42:40.875Z",
"requestId": "47d47151-9806-42de-a08d-bcf155bd913b",
"scope": "vibetools",
"tags": [
"konsole",
"console",
"bug",
"streaming"
],
"targetUser": "claude"
}