Yeachan-Heo/oh-my-codex · error · Error

sendToWorkerStdin: stdin is not writable

Error message

sendToWorkerStdin: stdin is not writable

What it means

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.

Source

Thrown at src/team/tmux-session.ts:3754

function assertWorkerTriggerText(text: string): void {
  if (text.length >= 200) {
    throw new Error('sendToWorker: text must be < 200 characters');
  }
  if (text.trim().length === 0) {
    throw new Error('sendToWorker: text must be non-empty');
  }
  if (text.includes(INJECTION_MARKER)) {
    throw new Error('sendToWorker: injection marker is not allowed');
  }
}

export function sendToWorkerStdin(
  stdin: Pick<NodeJS.WritableStream, 'write' | 'writable'> | null | undefined,
  text: string,
): void {
  assertWorkerTriggerText(text);
  if (!stdin || !stdin.writable) {
    throw new Error('sendToWorkerStdin: stdin is not writable');
  }
  stdin.write(`${text}\n`);
}

// Send SHORT text (<200 chars) to worker via tmux send-keys
// Validates: text < 200 chars, no injection marker
// Throws on violation
export async function sendToWorker(
  sessionName: string,
  workerIndex: number,
  text: string,
  workerPaneId?: string,
  workerCli?: TeamWorkerCli,
  expectedPanePid?: number,
  expectedTeamOwnerId?: string,
  hudPaneId?: string,
): Promise<void> {
  assertWorkerTriggerText(text);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Check worker.exitCode / worker.killed before sending; respawn the worker if it exited
  2. Monitor the worker's 'exit' and stdin 'close' events and stop/refresh the send path
  3. Guard with `if (worker.stdin?.writable)` before calling

Example fix

// before
sendToWorkerStdin(worker.stdin, text);

// after
if (worker.exitCode !== null || !worker.stdin?.writable) {
  worker = await spawnWorker();
}
sendToWorkerStdin(worker.stdin, text);
Defensive patterns

Strategy: type-guard

Validate before calling

if (worker.exitCode !== null) worker = await spawnWorker();

Type guard

function isStdinWritable(stdin: Pick<NodeJS.WritableStream, 'write' | 'writable'> | null | undefined): stdin is NodeJS.WritableStream { return !!stdin && stdin.writable === true; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/01670b7ae8f54a8b. Report an issue: GitHub.