anomalyco / anomalyco/opencode

Uncaught EIO/EPIPE on write crashes opencode when pty dies mid-write

Open
#40,043 0 comments 0 reactions 1 assignee View on GitHub

@kommander is already working on this.

Since Aug 1, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Bug

opencode (v1.18.10, bun 1.3.14, Linux) crashes hard with an uncaught exception when the underlying pty/pipe goes away while opencode is mid-write to stdout. Happened 3 times in one day, always the same stack.

Stack trace

EIO: i/o error, write
      fd: 13,
 syscall: "write",
   errno: -5,
    code: "EIO"

      at write (unknown:1:1)
      at writeFast (internal:fs/streams:345:38)
      at /$bunfs/root/chunk-ztxp1sde.js:95:38
      at ~effect/Effect/evaluate (/$bunfs/root/chunk-datjqaqc.js:25:4492)
      at runLoop (/$bunfs/root/chunk-datjqaqc.js:25:2045)
      at evaluate (/$bunfs/root/chunk-datjqaqc.js:25:1435)
      at /$bunfs/root/chunk-datjqaqc.js:25:5664
      at processTicksAndRejections (native:7:39)

Also observed repeatedly as non-fatal EPIPE (errno -32) bursts on fd 12 in the same session, via the same writeFast path, before the fatal EIO hit fd 13.

Root cause (as far as I traced it)

I run opencode inside a zellij pane spawned via a layout's command "opencode" directive (not through an interactive login shell). When the zellij session dies/exits (pty torn down) while opencode is still writing its TUI output, bun's raw write() on the underlying fd throws synchronously (EIO/EPIPE) instead of opencode/bun handling it as a normal stream-close condition. Since this happens outside any try/catch in the effect runLoop, it's an uncaught exception and kills the whole process — no graceful shutdown, no partial-session recovery, nothing logged except the raw stderr dump.

Expected behavior

Writing to a dead pty/pipe (EIO/EPIPE) should be treated like any other "peer went away" condition — swallow it and begin/complete a clean shutdown (or at minimum emit a catchable error), not propagate as an uncaught exception that kills the process mid-session.

Repro (best guess, not confirmed minimal repro)

  1. Launch opencode as the command of a zellij pane (not via a login shell) — e.g.:
    pane {
      command "opencode"
    }
    
  2. Let opencode run a TUI session, actively producing output (agent working, streaming tokens, etc.)
  3. Kill/detach/crash the zellij session out from under the pane while output is being written
  4. opencode dies with the EIO stack above, session progress lost, only recoverable via the stderr log

Workaround in use

Wrapping the zellij pane command as bash -lc "opencode" instead of a raw command "opencode" reduces exposure (gives opencode a shell-owned pty), but doesn't address the actual uncaught-throw-on-dead-pty behavior — if the terminal/pty dies for any other reason mid-write, the same crash should still reproduce.

Environment

  • opencode 1.18.10
  • bun 1.3.14
  • Linux (Fedora), Hyprland
  • Terminal multiplexer: zellij

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.