Skip to content

fix: handle EPIPE errors in StdioServerTransport to prevent process crash#1568

Open
stakeswky wants to merge 2 commits intomodelcontextprotocol:mainfrom
stakeswky:fix/stdio-epipe-crash
Open

fix: handle EPIPE errors in StdioServerTransport to prevent process crash#1568
stakeswky wants to merge 2 commits intomodelcontextprotocol:mainfrom
stakeswky:fix/stdio-epipe-crash

Conversation

@stakeswky
Copy link

Summary

Fix uncaught EPIPE exceptions in StdioServerTransport that crash the Node.js process when clients disconnect unexpectedly.

Problem

When a client disconnects while the server is writing to stdout, the stdout.write() call throws an EPIPE error. Since there's no error handler, this becomes an uncaught exception that crashes the entire Node.js process.

Error: write EPIPE
    at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:94:16)

Root Cause

  1. The send() method calls this._stdout.write() without any error handling
  2. The start() method doesn't register an error listener on _stdout
  3. When the client disconnects, any pending or new writes fail with EPIPE

Fix

  1. Add stdout error listener in start(): Catches EPIPE and other stdout errors, triggers graceful close
  2. Handle write errors in send(): Wrap write operations with error handling, reject the promise on failure
  3. Clean up in close(): Remove stdout error listener to prevent leaks

Behavior

  • When stdout errors occur (EPIPE, etc.), the transport now:
    • Calls the onerror callback with the error
    • Triggers graceful close() to clean up resources
    • Rejects pending send() promises instead of crashing

This allows servers to handle client disconnections gracefully without process crashes.

Fixes #1564

User added 2 commits February 22, 2026 16:12
Per MCP spec, calling a nonexistent tool should return a JSON-RPC Error
(code -32602), not a JSON-RPC Result with isError: true.

Previously, only UrlElicitationRequired errors were re-thrown; all other
ProtocolErrors (including tool/disabled checks) were swallowed and wrapped
in a CallToolResult. Now all ProtocolErrors propagate as JSON-RPC errors,
which is the correct behavior per the specification.

Fixes modelcontextprotocol#1510
Add error handling for stdout write failures to prevent uncaught EPIPE
exceptions when clients disconnect unexpectedly.

Changes:
- Add stdout error listener in start() to catch EPIPE and other errors
- Trigger graceful close when stdout errors occur
- Handle write errors in send() by rejecting the promise
- Clean up stdout error listener in close()

This prevents the Node.js process from crashing when a client disconnects
while the server is writing to stdout.

Fixes modelcontextprotocol#1564
@stakeswky stakeswky requested a review from a team as a code owner February 22, 2026 08:16
@changeset-bot
Copy link

changeset-bot bot commented Feb 22, 2026

⚠️ No Changeset found

Latest commit: af8ccdc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

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.

Unhandled 'EPIPE' in 'StdioServerTransport' causes fatal process crash on client disconnect

1 participant