Skip to content

fix: handle EPIPE errors in StdioServerTransport gracefully#1644

Open
MaxwellCalkin wants to merge 1 commit intomodelcontextprotocol:mainfrom
MaxwellCalkin:fix-stdio-epipe-crash
Open

fix: handle EPIPE errors in StdioServerTransport gracefully#1644
MaxwellCalkin wants to merge 1 commit intomodelcontextprotocol:mainfrom
MaxwellCalkin:fix-stdio-epipe-crash

Conversation

@MaxwellCalkin
Copy link

@MaxwellCalkin MaxwellCalkin commented Mar 8, 2026

Note: This PR was authored by Claude (AI), operated by @MaxwellCalkin.

Fixes #1564

Problem

When an MCP client disconnects abruptly (closes stdio pipes before the server can write a response), stdout.write() throws an unhandled EPIPE error that crashes the entire Node.js process:

node:events:486
      throw er; // Unhandled 'error' event
      ^
Error: EPIPE: broken pipe, write
    at Socket._write (node:internal/net:75:18)
    ...
    at StdioServerTransport.send (...)

This happens because StdioServerTransport has no error event listener on the stdout stream, and stdout.write() in send() is not wrapped in error handling.

Fix

Three changes to StdioServerTransport:

  1. Add _onstdouterror handler — an arrow function (matching the existing pattern for _ondata/_onerror) that forwards stdout errors to onerror and triggers a clean close().

  2. Register/unregister the listenerstart() adds this._stdout.on('error', this._onstdouterror); close() removes it with .off().

  3. Wrap stdout.write() in try/catch in send() — if write() throws synchronously, the promise rejects instead of crashing the process.

This matches how the client-side StdioClientTransport already handles errors on stdin and stdout.

Tests

Added two new tests:

  • "should handle stdout write errors gracefully" — verifies that async write errors (callback with error) trigger onerror and close()
  • "should handle synchronous stdout write throws gracefully" — verifies that synchronous write() throws are caught and the promise rejects

Changeset

Included a patch changeset for @modelcontextprotocol/server.

When a client disconnects abruptly (closes stdio pipes before the server
can write a response), stdout.write() throws an unhandled EPIPE error
that crashes the entire Node.js process.

This commit:
- Adds an error event listener on stdout that forwards errors to
  onerror and triggers a clean close()
- Wraps stdout.write() in try/catch in send() to catch synchronous
  write errors and reject the promise instead of crashing
- Removes the stdout error listener in close() to prevent leaks

Fixes modelcontextprotocol#1564
@MaxwellCalkin MaxwellCalkin requested a review from a team as a code owner March 8, 2026 06:53
@changeset-bot
Copy link

changeset-bot bot commented Mar 8, 2026

🦋 Changeset detected

Latest commit: 75c8800

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/express Patch
@modelcontextprotocol/hono Patch
@modelcontextprotocol/node Patch

Not sure what this means? Click here to learn what changesets are.

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 8, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1644

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1644

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1644

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1644

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1644

commit: f81337b

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