PrefectHQ/fastmcp · error · RuntimeError

FastMCP instance is no longer available

Error message

FastMCP instance is no longer available

What it means

LowLevelServer keeps only a weak reference (weakref.ref) to the FastMCP instance to avoid a circular reference (fastmcp_slim/fastmcp/server/low_level.py:459). The `fastmcp` property dereferences that weakref, and if the FastMCP object has been garbage collected it raises this RuntimeError instead of returning None. It is a lifecycle bug in user code, not an MCP protocol error: the low-level server outlived the FastMCP server it wraps.

Source

Thrown at fastmcp_slim/fastmcp/server/low_level.py:511

        # the server was constructed without an explicit `request_state_security`
        # policy, seal under a per-process ephemeral key (single-process
        # deployments); multi-replica deployments pass a shared-key policy. The
        # low-level server always has a name (FastMCP autogenerates one), so the
        # audience claim is always populated.
        security = fastmcp._request_state_security or RequestStateSecurity.ephemeral()
        self.middleware.append(
            cast(
                "ServerMiddleware[LifespanResultT]",
                RequestStateBoundary(security, default_audience=self.name),
            )
        )

    @property
    def fastmcp(self) -> FastMCP:
        """Get the FastMCP instance."""
        fastmcp = self._fastmcp_ref()
        if fastmcp is None:
            raise RuntimeError("FastMCP instance is no longer available")
        return fastmcp

    def create_initialization_options(
        self,
        notification_options: NotificationOptions | None = None,
        experimental_capabilities: dict[str, dict[str, Any]] | None = None,
        extensions: dict[str, dict[str, Any]] | None = None,
    ) -> InitializationOptions:
        # ensure we use the FastMCP notification options
        if notification_options is None:
            notification_options = self.notification_options
        return super().create_initialization_options(
            notification_options=notification_options,
            experimental_capabilities=experimental_capabilities,
            extensions=extensions,
        )

    def get_capabilities(

View on GitHub (pinned to 1f02114297)

Solutions

  1. Keep a strong, long-lived reference to the FastMCP instance (module-level variable, app state, or fixture) for as long as the LowLevelServer/session is alive.
  2. Construct and store both objects together: `mcp = FastMCP('demo'); llm = LowLevelServer(mcp)` and keep `mcp` in scope.
  3. If the reference must be dropped, also drop the LowLevelServer/session at the same time so no dangling dereference occurs.
  4. Check for test-framework garbage collection (e.g. pytest fixtures that del the server) and use a session-scoped fixture to hold the FastMCP instance.

Example fix

# before
server = LowLevelServer(FastMCP('demo'))  # FastMCP instance immediately unreachable
run(server)
# after
mcp = FastMCP('demo')          # strong reference kept alive
server = LowLevelServer(mcp)
run(server)
Defensive patterns

Strategy: type-guard

Validate before calling

import weakref
ref = getattr(server, '_fastmcp_ref', None)
if not isinstance(ref, weakref.ref) or ref() is None:
    raise RuntimeError('FastMCP instance was garbage collected; recreate both objects')

Type guard

def fastmcp_alive(server) -> bool:
    ref = getattr(server, '_fastmcp_ref', None)
    return ref is not None and ref() is not None

Try / catch

try:
    mcp = server.fastmcp
except RuntimeError as e:
    if 'no longer available' in str(e):
        mcp = FastMCP('demo')  # recreate and keep a strong reference
        server = LowLevelServer(mcp)
    else:
        raise

Prevention

When it happens

Trigger: Accessing `server.fastmcp` (or any code path that touches it, e.g. `create_initialization_options`/`get_capabilities` via the experimental-capability merge) after the FastMCP instance has been garbage collected — typically when only the LowLevelServer was kept alive and the FastMCP object was never assigned to a long-lived variable.

Common situations: Scripts that build a FastMCP server inline (`LowLevelServer(FastMCP('demo'))` without storing the FastMCP instance); test suites where the FastMCP fixture goes out of scope while the session/server object persists; refactors that dropped a module-level or app-level reference to the server.

Related errors


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