What happened?
With the default keep_alive=True, cancelling a direct StdioTransport.connect_session() call while its first connection is still starting can leave the transport in a state where a later context receives None instead of a ClientSession.
The local reproduction cancels after the background _connect_task exists but before _ready_event is set, then calls connect_session() again on the same transport. The background connection can finish, but the second caller still receives no session: connect() sees the existing task and returns without awaiting its session future, while _session was never assigned by the cancelled caller. A client using the transport then fails with “Client is not connected” until the transport is closed and reconnected.
This is limited to direct connect_session() use. The ordinary async with Client(...) entry path has cancellation cleanup and passed the control case. A later context should await or adopt the in-flight session, or discard the abandoned startup and reconnect; it should not yield None.
Example Code
Version Information
- FastMCP main: `5baeacfe20eca735cb949564b4915f93a622b916`
- Python: 3.12.15
- OS: macOS 15.0.1 arm64
What happened?
With the default
keep_alive=True, cancelling a directStdioTransport.connect_session()call while its first connection is still starting can leave the transport in a state where a later context receivesNoneinstead of aClientSession.The local reproduction cancels after the background
_connect_taskexists but before_ready_eventis set, then callsconnect_session()again on the same transport. The background connection can finish, but the second caller still receives no session:connect()sees the existing task and returns without awaiting its session future, while_sessionwas never assigned by the cancelled caller. A client using the transport then fails with “Client is not connected” until the transport is closed and reconnected.This is limited to direct
connect_session()use. The ordinaryasync with Client(...)entry path has cancellation cleanup and passed the control case. A later context should await or adopt the in-flight session, or discard the abandoned startup and reconnect; it should not yieldNone.Example Code
Version Information