fix: prevent stack overflow in transport close with re-entrancy guard#1788
Open
claygeo wants to merge 3 commits intomodelcontextprotocol:mainfrom
Open
fix: prevent stack overflow in transport close with re-entrancy guard#1788claygeo wants to merge 3 commits intomodelcontextprotocol:mainfrom
claygeo wants to merge 3 commits intomodelcontextprotocol:mainfrom
Conversation
When 15-25+ transports close simultaneously, close() -> onclose() triggers the Protocol layer to call close() again recursively, causing a stack overflow. The process stays alive but becomes unresponsive. Three problems in the original close(): 1. No re-entrancy guard: onclose() can call back into close() 2. Iterating _streamMapping while cleanup callbacks delete from it 3. No error isolation: one failing cleanup prevents others The fix: - Adds _closing boolean guard to prevent recursive calls - Snapshots stream values into an array and clears the map before iterating, avoiding mutation-during-iteration - Wraps each cleanup() in try/catch so one failure doesn't block remaining stream cleanups - Calls onclose() only after all cleanup is complete Note: two prior PRs (modelcontextprotocol#1700, modelcontextprotocol#1705) attempted similar fixes but were closed. This implementation combines the best elements of both: the re-entrancy guard from modelcontextprotocol#1700 and the snapshot-before-clear pattern from modelcontextprotocol#1705, with the addition of error isolation. Closes modelcontextprotocol#1699
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
Replace Array.from(map.values()) with [...map.values()] to fix the unicorn/prefer-spread ESLint rule.
felixweinberger
requested changes
Mar 27, 2026
Contributor
felixweinberger
left a comment
There was a problem hiding this comment.
Thanks - can you demonstrate this with a test?
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 15-25+ transports close simultaneously (e.g., server shutdown under load),
close()→onclose()triggers the Protocol layer to callclose()again recursively, causing aRangeError: Maximum call stack size exceeded. The process stays alive but becomes completely unresponsive.Root cause (3 issues in
close())this.onclose?.()at line 911 can trigger the Protocol layer to callclose()again, creating infinite recursioncleanup()callback callsthis._streamMapping.delete(...), mutating the map being iterated byfor...ofcleanup()throws, remaining streams are never cleaned upReproduction
Start an MCP server with 15+ concurrent SSE connections, then trigger a shutdown. The close cascade causes stack overflow:
Solution
Why prior PRs (#1700, #1705) were insufficient
_closingguard but didn't snapshot-before-clearChanges
packages/server/src/server/streamableHttp.ts— Rewroteclose()with re-entrancy guard, snapshot-before-clear, and try/catch error isolationTest Plan
Closes #1699