Uh oh!
There was an error while loading. Please reload this page.
Guard NetworkTransport connect continuation against stale reconnectio… - #178
Guard NetworkTransport connect continuation against stale reconnectio…#178algal wants to merge 1 commit into
Conversation
DePasqualeOrg
commented
Jan 20, 2026
This issue, along with many others, has already been resolved in my fork, which @movetz, @stallent, and I are discussing merging into this repository. Instead of using a checked continuation with callback-based state handling (which requires the UUID-based guard proposed in this PR), my fork uses privatefunc waitForConnectionReady()asyncthrows{letstateStream= AsyncStream<NWConnection.State>{ continuation in
connection.stateUpdateHandler ={ state in
continuation.yield(state)switch state {case.ready,.failed,.cancelled:
continuation.finish()default:break}}}forawaitstatein stateStream {switch state {case.ready:returncase.failed(let error):throw error
case.cancelled:throwMCPError.internalError("Connection cancelled")
// ...
}}}This approach inherently avoids the double-resume race condition because:
|
algal
commented
Jan 20, 2026
@DePasqualeOrg Cool! It sounds like your solution is better. I hope it is merged. :) |
It won't be, unless it's replicated by someone else, because the new maintainers of this package want to take a patchwork approach to improvements. My full-featured fork will continue to be developed separately here: DePasqualeOrg/swift-mcp |
toasterbook88
commented
Feb 24, 2026
Maintainer triage on February 24, 2026: this transport fix looks valuable but currently conflicts with main. Please rebase onto current main and rerun CI for a decision. |
This PR fixes a crash in
NetworkTransport.connect()where a delayed reconnection task could resume a stale continuation after a new connect attempt started, causing a crash.Motivation and Context
The bug occurs when a delayed reconnection task from a previous connect attempt runs after a new connect attempt has started.
While using iMCP.app, I saw that app crash when a client (
imcp-server) started and then died quickly. Crash logs showedCheckedContinuation.resume(throwing:)fromNetworkTransport.handleReconnection. What was happening was that whenNetworkTransport.connect()is called, it uses a checked continuation and schedules reconnection work with backoff on failure/cancellation. If a reconnect attempt is scheduled and a newconnect()call happens before that delayed task fires, the old task can still resume the previous continuation. That double‑resumes aCheckedContinuationand traps (EXC_BREAKPOINT / SIGTRAP).How Has This Been Tested?
Trying this with the iMCP.app is the most easy way to repro the crash, and what I used to ensure that the fix removed the crash. You just run iMCP, then try running its
imcp-serverand CTRL-C it immmediately a few times. Eventually, this brings down the iMCP process as well.I have not produced a test which reproduces the bug in isolation, outside of iMCP, but I can do so if that would help. The sequence would work as follows
Repro (before fix):
NetworkTransport(default reconnection config).connect(), then cancel the underlying connection quickly enough to enterhandleReconnection.connect()again before the reconnection backoff delay elapses.EXC_BREAKPOINT / SIGTRAP).I found that
swift testfails on Swift 6.2.3 due to existing strict-concurrency errors inTests/MCPTests/ClientTests.swift.How this fix works
The continuation‑resume guard was a single boolean reset on each
connect()call. Delayed reconnection tasks from a previous connect attempt could still run after a new connect had reset the guard, allowing a second resume on the old continuation.This fix works as follows:
connectContinuationID).handleConnectionReady/Failed/CancelledandhandleReconnection.Breaking Changes
None.
Besides the fix, behavior is unchanged; this only prevents stale tasks from resolving an old continuation.
This is an internal-only change; the only observable difference is that stale reconnection tasks no longer resolve a previous
connect()attempt.Types of changes
Checklist
Additional context on use of AI
I (algal) have reviewed this PR, performed the before/after tests above, and I believe the analysis I have stated above is correct. I also have a background in Swift and of native development. However, I am not deeply familiar with this code base, and I did rely on AI in the developing this fix and drafting this PR text.
I know there's a lot of slop PRs flying around these days, so I wanted to be transparent about that. I am happy to be corrected or steered