CommandCodeAI / CommandCodeAI/command-code

Uncaught `write EOF` on Windows kills the session after a large MCP tool result

Offen
#859 1 Kommentar 0 Reaktionen 1 zugewiesene Person Auf GitHub ansehen

@naymurdev arbeitet bereits daran.

Seit 16.9.2026.

windows
Vorherrschende Sprache
Keine Sprachdaten
Sterne
4k
Forks
350
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

Summary

On Windows, a large MCP tool result makes the CLI die with an uncaught Error: write EOF
(WriteWrap.onWriteComplete, node:internal/stream_base_commons:87:19) and exit immediately.
No recovery: any uncaught exception is fatal by design.

Environment

  • Command Code v1.54.0 (latest on npm at the time)
  • OS: Windows (win32), binary cmdc, shell PowerShell, terminal Windows Terminal
  • Model: deepseek/deepseek-v4.1-flash (effort high)
  • MCP server involved: clickupHTTP transport with OAuth (https://mcp.clickup.com/mcp)

Steps to reproduce

  1. Configure the ClickUp MCP server (http + OAuth) and complete auth.
  2. In a session, ask for the details of a large task, so the agent issues:
{
  "tool": "mcp__clickup__clickup_get_task",
  "input": {
    "task_id": "SP-5250",
    "include": ["description", "custom_fields", "checklists", "subtasks", "attachments"]
  }
}

(note: the tool is first loaded via search_tools with select:mcp__clickup__clickup_get_task)

  1. The tool returns ~102 KB of JSON (the task has a ~50 KB markdown_description).

Observed

The tool result is received, persisted to the session transcript and spilled to
%TEMP%\commandcode\toolout\ (113,649 bytes) — and immediately after, while the loop continues
(building/sending the next turn), the CLI prints:

CRITICAL: Uncaught Exception!
ERROR  -> Error
REASON -> write EOF
STACK  -> Error: write EOF
            at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:87:19)

and the process exits (process.exit(1)). The TUI closes without warning.

Trace ID: 1ebb760a09a72cbe96dc6b03e148b0fc
Session: 98584fe6-abfb-4ff2-a888-05210eb72139
A second session crashed identically on the same payload (0138f4fb-45bc-481f-bb4d-c9c4f97a5c4d,
traceIds include several additional ids).

Expected

A failed write on a network socket should surface as a tool/network error (retry or reported error),
not as an uncaught exception that kills the whole session, especially since the payload is already
persisted on disk.

Analysis

write EOF is libuv UV_EOF on a write completion — the Windows equivalent of EPIPE: something
wrote to a stream whose peer had already closed it. It is a socket/pipe write, not a terminal write.

The CLI already has a whitelist for exactly this class of error, in dist/cli.mjs:

var zD = ["EPIPE","ECONNRESET","EOF"];
function isBenignStreamWriteError(e){
  ...
  return !!zD.some(e => r.includes(`write ${e}`)) || (t.syscall === "write" && zD.includes(n));
}
function handleStdoutError(e){ if(!isBenignStreamWriteError(e)) throw e; exitAfterModHostTeardown(0) }
function handleStderrError(e){ if(!isBenignStreamWriteError(e)) throw e }
function handleUnhandledErrors(){
  process.stdout.on("error", handleStdoutError);
  process.stderr.on("error", handleStderrError);
  process.on("unhandledRejection", ...);
  process.on("uncaughtException", ...);
}

isBenignStreamWriteError does match write EOF, but handleUnhandledErrors() attaches it
only to process.stdout / process.stderr. A write EOF emitted on any other stream — the HTTP
socket to the API/model, the MCP transport, a child pipe — is not covered, so it reaches the
uncaughtException handler and becomes fatal. If the same error occurred on stdout it would be
swallowed silently (exit(0), no banner), which confirms the failing stream is a socket and not the
TUI.

Correlation with size: the crash happens right after a ~102 KB tool result and only on this call.
Everything else about the setup looks healthy — calling the ClickUp MCP endpoint directly with the
stored token returns 200 OK in 1.4 s, connection: keep-alive, content-type: application/json,
and the OAuth access token is valid until 2036, so it is not a re-auth/refresh issue. The MCP timeout
is not involved either (default Nt = 8e4 = 80 s, the crash happened at ~17 s).

Suggested fix

Treat benign stream write errors (EPIPE / ECONNRESET / EOF) as non-fatal globally (or attach
the same guard to the network/transport streams), and/or wrap the socket write path so an aborted or
keep-alive-reused connection surfaces as a retriable request error instead of an uncaught exception.

Notes

  • No local logs existed because DEBUG was not set; I reproduced this twice so it is deterministic.
  • The full tool output survives in %TEMP%\commandcode\toolout\, and the transcript in
    ~/.commandcode/projects/<slug>/<session-id>.jsonl ends exactly at the tool_result message
    (102,727 bytes), so awaiting-the-next-step is where it dies.
  • Happy to run further instrumented reproductions (DEBUG=true, CMD_FLICKER_DEBUG=1) if useful.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.