{"record":{"id":"01670b7ae8f54a8b","repo":"Yeachan-Heo/oh-my-codex","slug":"sendtoworkerstdin-stdin-is-not-writable","errorCode":null,"errorMessage":"sendToWorkerStdin: stdin is not writable","messagePattern":"sendToWorkerStdin: stdin is not writable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/tmux-session.ts","lineNumber":3754,"sourceCode":"function assertWorkerTriggerText(text: string): void {\n  if (text.length >= 200) {\n    throw new Error('sendToWorker: text must be < 200 characters');\n  }\n  if (text.trim().length === 0) {\n    throw new Error('sendToWorker: text must be non-empty');\n  }\n  if (text.includes(INJECTION_MARKER)) {\n    throw new Error('sendToWorker: injection marker is not allowed');\n  }\n}\n\nexport function sendToWorkerStdin(\n  stdin: Pick<NodeJS.WritableStream, 'write' | 'writable'> | null | undefined,\n  text: string,\n): void {\n  assertWorkerTriggerText(text);\n  if (!stdin || !stdin.writable) {\n    throw new Error('sendToWorkerStdin: stdin is not writable');\n  }\n  stdin.write(`${text}\\n`);\n}\n\n// Send SHORT text (<200 chars) to worker via tmux send-keys\n// Validates: text < 200 chars, no injection marker\n// Throws on violation\nexport async function sendToWorker(\n  sessionName: string,\n  workerIndex: number,\n  text: string,\n  workerPaneId?: string,\n  workerCli?: TeamWorkerCli,\n  expectedPanePid?: number,\n  expectedTeamOwnerId?: string,\n  hudPaneId?: string,\n): Promise<void> {\n  assertWorkerTriggerText(text);","sourceCodeStart":3736,"sourceCodeEnd":3772,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/tmux-session.ts#L3736-L3772","documentation":"Thrown by sendToWorkerStdin when the provided stdin stream is null/undefined or its .writable property is false — i.e. the worker process's stdin pipe is closed or destroyed, so text cannot be delivered. This is a precondition failure on the stream argument, checked after trigger-text validation passes.","triggerScenarios":"Calling sendToWorkerStdin(worker.stdin, text) after the worker process exited (stdin closed), passing null, or passing a stream that has been .end()ed or destroyed.","commonSituations":"Worker process crashed earlier and the orchestrator still holds a stale reference; race between worker exit and next send; stream explicitly closed on shutdown but send loop still running.","solutions":["Check worker.exitCode / worker.killed before sending; respawn the worker if it exited","Monitor the worker's 'exit' and stdin 'close' events and stop/refresh the send path","Guard with `if (worker.stdin?.writable)` before calling"],"exampleFix":"// before\nsendToWorkerStdin(worker.stdin, text);\n\n// after\nif (worker.exitCode !== null || !worker.stdin?.writable) {\n  worker = await spawnWorker();\n}\nsendToWorkerStdin(worker.stdin, text);","handlingStrategy":"type-guard","validationCode":"if (worker.exitCode !== null) worker = await spawnWorker();","typeGuard":"function isStdinWritable(stdin: Pick<NodeJS.WritableStream, 'write' | 'writable'> | null | undefined): stdin is NodeJS.WritableStream { return !!stdin && stdin.writable === true; }","tryCatchPattern":null,"preventionTips":["Listen for worker 'exit' and stdin 'close' events to invalidate references","Check writable right before every write"],"tags":["stdin","stream-closed","process-exited","nodejs"],"backgroundTag":"writable-stream-closed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}