fix(channel): exit when parent dies instead of spinning on EPIPE - #4
Open
JefBronze wants to merge 1 commit into
Open
fix(channel): exit when parent dies instead of spinning on EPIPE#4JefBronze wants to merge 1 commit into
JefBronze wants to merge 1 commit into
Conversation
When the Claude Code session that spawned the MCP server exits, the server's stdio sockets close. Every stderr/stdout write then throws EPIPE; the uncaughtException handler called log(), which wrote to the dead stderr and threw again, producing an infinite exception loop at 100% CPU. Three such orphans were found pinned for days on a fanless MacBook Air, triggering repeated thermal-emergency sleeps. - log(): no-op once stdio is known dead; swallow write errors - attach error handlers to stdin/stdout/stderr for EPIPE/ECONNRESET/EIO/EBADF - shut down gracefully on stdin end/close - poll process.ppid and shut down if reparented (parent SIGKILLed) - uncaughtException: treat stdio errors as parent death, never re-log - hard exit after 3s if graceful shutdown hangs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxRbndHbg5wB5BQ591TZAP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the Claude Code session that spawned the MCP server exits, the server is orphaned with its stdio sockets closed. Every
stdout/stderrwrite then throwsEPIPE. TheuncaughtExceptionhandler callslog(), which writes to the deadstderr, which throwsEPIPEagain, and the process spins in an infinite exception loop at 100% CPU.Observed on macOS: three such orphans (one running 12 days, 88 CPU-hours) pinned three cores on a fanless MacBook Air, causing repeated
Dark Wake Thermal Emergencysleeps and throttled charging.SIGTERMcould not stop them either, becauseshutdown()also logs to the deadstderr.A
sampleof a stuck process shows every frame inTriggerUncaughtException→ handler →stderr.write→TriggerUncaughtException.Fix
log()becomes a no-op once stdio is known dead and swallows write errors.errorhandlers onstdin/stdout/stderrtreatEPIPE,ECONNRESET,EIO,EBADFas parent death.stdinend/closetriggersgracefulShutdown, so the offline heartbeat is still written.process.ppidwatchdog (same interval as heartbeat) catches the case where the parent isSIGKILLed and no EOF arrives promptly.uncaughtExceptionhandler exits on stdio errors instead of re-logging them.process.exit(1)after 3 s if graceful shutdown hangs.Testing
In an isolated
CC2CC_BRIDGE_DIR:stdinclosed after 2 s → server exited within the heartbeat interval.SIGKILLed → orphan exited within ~8 s via the ppid watchdog.Both previously left the process alive at 100% CPU.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TxRbndHbg5wB5BQ591TZAP