Skip to content

fix(dev): swallow benign EIO/EPIPE pipe writes on dev restart - #128

Merged
big-guy merged 1 commit into
ness-dev:mainfrom
big-guy:fix-dev-restart-eio
Jun 1, 2026
Merged

fix(dev): swallow benign EIO/EPIPE pipe writes on dev restart#128
big-guy merged 1 commit into
ness-dev:mainfrom
big-guy:fix-dev-restart-eio

Conversation

@big-guy

@big-guy big-guy commented May 29, 2026

Copy link
Copy Markdown
Collaborator

The bug

On dev stop/restart (npm run dev), electron-vite tears down the Electron child process and closes the read end of its stdout/stderr pipe. An in-flight console.log then writes to a broken pipe; on macOS libuv returns EIO (rather than EPIPE). The error surfaces synchronously out of console.logWritable.writeafterWriteDispatched, and with nothing catching it, it became an uncaught exception that crashed the main process on every dev teardown:

Uncaught Exception:
Error: write EIO
    at afterWriteDispatched (node:internal/stream_base_commons:159:15)
    ...
    at console.value (node:internal/console/constructor:313:16)
    at console.log (node:internal/console/constructor:416:26)
    at Object.log (.../out/main/chunks/persistence-*.js:87:10)

The crashing frame is src/main/debug.ts's log() (console.log(line)). The appendFileSync below it was already try/caught, but the console.log was unguarded — and so are the many other console.* calls across main, so a fix scoped only to debug.ts would be insufficient.

The fix

Install both a synchronous-throw guard (uncaughtException) and async stream 'error' handlers at the very top of src/main/index.ts, before anything can log. A stream-error handler alone can't catch the synchronous throw; a try/catch in debug.ts alone wouldn't cover the other call sites. Strictly scoped to EIO/EPIPE on the std streams so real crashes still propagate.

Purely additive — there were no pre-existing uncaughtException / stdout-error handlers in src/.

Verification

  • npm run typecheck — passed
  • npx electron-vite build — clean
  • npx vitest run — 115 files / 1333 tests passed
  • Manual: ran npm run dev, booted, killed to trigger teardown — no EIO / uncaught exception in the log

🤖 Generated with Claude Code

@big-guy big-guy self-assigned this May 29, 2026
@big-guy

big-guy commented May 29, 2026

Copy link
Copy Markdown
Collaborator Author

This doesn't happen on every restart, but when restarting the dev app, I get these stacktraces that prevent the app from closing (I use Force Quit to close it). If I believe Claude, this is a dev-only thing.

@big-guy
big-guy requested a review from frenchie4111 May 29, 2026 18:58
@frenchie4111

Copy link
Copy Markdown
Collaborator

@big-guy
Are you killing it from the terminal (Ctrl + C) or are you quitting dev? I have never run into this but I always CMD + Q the dev screen (idk why but I have that habit from early on in development, which might mean it was from this error)

@big-guy

big-guy commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator Author

@big-guy Are you killing it from the terminal (Ctrl + C) or are you quitting dev? I have never run into this but I always CMD + Q the dev screen (idk why but I have that habit from early on in development, which might mean it was from this error)

I suspect that's what Claude was doing. It would make a change that required a restart and when stopping, I'd see these errors and have two copies running (one with this error and one with the new changes)

electron-vite closes the Electron child's stdout/stderr pipe on
stop/restart. An in-flight console write then fails — synchronously
out of console.log on macOS with EIO (libuv returns EIO, not EPIPE),
which surfaced as an uncaught exception that crashed the main process
on every dev teardown.

Install both a synchronous-throw guard (uncaughtException) and async
stream 'error' handlers at the top of main, before anything can log.
The stream handler alone can't catch the sync throw; a try/catch in
debug.ts alone wouldn't cover the many other console.* call sites in
main. Strictly scoped to EIO/EPIPE on the std streams so real crashes
still propagate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@big-guy
big-guy force-pushed the fix-dev-restart-eio branch from 2e6c3e4 to eb091e4 Compare June 1, 2026 21:31
@big-guy
big-guy merged commit 9a6b217 into ness-dev:main Jun 1, 2026
1 check passed
@big-guy
big-guy deleted the fix-dev-restart-eio branch June 1, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants