PrefectHQ/fastmcp · error · RuntimeError

Session task completed without exception but connection fail

Error message

Session task completed without exception but connection failed

What it means

After spawning the background session task, the client waits briefly for it to fail or become ready. If the task finishes without raising but the connection still isn't established, `_connect` raises this RuntimeError, since a completed session task with no exception implies the connection sequence failed silently. It is an internal invariant check.

Source

Thrown at fastmcp_slim/fastmcp/client/client.py:1040

                                    f"Error closing transport after cancellation: {e}"
                                )

                    raise

                session_task = self._session_state.session_task
                if not session_task.done() and self._session_state.session is None:
                    # `_session_runner` sets `ready_event` from its `finally`,
                    # so a failed connect can wake the wait above before the
                    # task is marked done. No session means the connect failed,
                    # so let the task settle and report the failure here rather
                    # than letting the raw transport error escape on the next
                    # request.
                    await asyncio.wait([session_task], timeout=3)

                if session_task.done():
                    exception = session_task.exception()
                    if exception is None:
                        raise RuntimeError(
                            "Session task completed without exception but connection failed"
                        )
                    failure = _connection_failure(exception)
                    if failure is exception:
                        raise exception
                    raise failure from exception

            self._session_state.nesting_counter += 1

        return self

    async def _disconnect(self, force: bool = False):
        """
        Disconnect from session using reference counting.

        This method implements proper cleanup for reentrant context managers:
        - Decrements reference counter for normal exits
        - Only stops session when counter reaches 0 (no more active contexts)

View on GitHub (pinned to 1f02114297)

Solutions

  1. Check that the server process/endpoint stays alive after startup (it may be exiting cleanly too early)
  2. Inspect server startup logs and exit code for silent early exits
  3. Update fastmcp and the MCP SDK — this usually indicates a transport/session bug
  4. Test with a different transport to isolate the fault
Defensive patterns

Strategy: try-catch

Validate before calling

proc = await anyio.open_process([cmd])
await anyio.sleep(1)
if proc.returncode is not None:
    raise RuntimeError(f"server exited early with code {proc.returncode}")

Try / catch

try:
    async with client:
        ...
except RuntimeError as e:
    if "without exception but connection failed" in str(e):
        logger.error("silent transport startup failure; check server exit behavior")
    raise

Prevention

When it happens

Trigger: The session coroutine completes normally before signaling a ready connection — e.g. the transport exits early without error, or closes streams gracefully during startup.

Common situations: A stdio server exiting cleanly (code 0) immediately on startup without serving; a transport that closes its streams during initialization; races between session readiness and task completion.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/9892e681064c4d11. Report an issue: GitHub.