PrefectHQ/fastmcp · critical · RuntimeError

Task group is not initialized. Make sure to use run().

Error message

Task group is not initialized. Make sure to use run().

What it means

The StreamableHTTP ASGI wrapper requires its StreamableHTTPSessionManager to have been started via run(), which initializes the underlying anyio task group. If session_manager is None (run() never called) the ASGI app raises RuntimeError 'Task group is not initialized. Make sure to use run().' and logs it as an internal server error.

Source

Thrown at fastmcp_slim/fastmcp/server/http.py:89

        # The SDK reads `self.event_store` once when constructing each transport.
        # A fresh adapter gives that transport a private stream namespace.
        return SessionScopedEventStore(self._shared_event_store, session_id=uuid4().hex)

    @event_store.setter
    def event_store(self, event_store: EventStore | None) -> None:
        self._shared_event_store = event_store


class StreamableHTTPASGIApp:
    """ASGI application wrapper for Streamable HTTP server transport."""

    def __init__(self, session_manager: StreamableHTTPSessionManager | None):
        self.session_manager = session_manager

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        try:
            if self.session_manager is None:
                raise RuntimeError(
                    "Task group is not initialized. Make sure to use run()."
                )
            await self.session_manager.handle_request(scope, receive, send)
        except RuntimeError as e:
            if str(e) == "Task group is not initialized. Make sure to use run().":
                logger.error(
                    f"Original RuntimeError from mcp library: {e}", exc_info=True
                )
                new_error_message = (
                    "FastMCP's StreamableHTTPSessionManager task group was not initialized. "
                    "This commonly occurs when the FastMCP application's lifespan is not "
                    "passed to the parent ASGI application (e.g., FastAPI or Starlette). "
                    "Please ensure you are setting `lifespan=mcp_app.lifespan` in your "
                    "parent app's constructor, where `mcp_app` is the application instance "
                    "returned by `fastmcp_instance.http_app()`. \\n"
                    "For more details, see the FastMCP ASGI integration documentation: "
                    "https://gofastmcp.com/deployment/asgi"
                )

View on GitHub (pinned to 1f02114297)

Solutions

  1. Run the app through its lifespan: use fastmcp http_app() with a server that supports lifespan (uvicorn app, not just the raw ASGI callable).
  2. In a custom Starlette app, use its .router/lifespan context so FastMCP's lifespan (which calls session_manager.run()) executes before requests.
  3. Enable lifespan in the deployment (e.g. remove lifespan='off' from uvicorn/gunicorn config).
  4. In tests, wrap requests in the app's lifespan context (e.g. asgi_lifespan.LifespanManager).

Example fix

// before
uvicorn config: gunicorn -k uvicorn.workers.UvicornWorker app:asgi_app --no-lifespan
// after
# let lifespan run so session_manager.run() initializes the task group
gunicorn -k uvicorn.workers.UvicornWorker app:asgi_app  # lifespan enabled
Defensive patterns

Strategy: validation

Validate before calling

# ensure lifespan ran before serving
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
    async with fastmcp_app.lifespan(fastmcp_app):
        yield

Type guard

def session_manager_running(app) -> bool:
    mgr = getattr(app.state, "session_manager", None)
    return mgr is not None and getattr(mgr, "_task_group", None) is not None

Try / catch

try:
    await app(scope, receive, send)
except RuntimeError as e:
    if "Task group is not initialized" in str(e):
        logger.error("Serve via fastmcp http_app() with lifespan enabled")
    raise

Prevention

When it happens

Trigger: Mounting the http_app/ASGI callable in a server (uvicorn, starlette) without awaiting the lifespan that calls session_manager.run(); running the ASGI app outside its lifespan context; constructing the wrapper manually without a manager.

Common situations: Deploying with gunicorn without lifespan support (or lifespan='off'); mounting the FastMCP http_app in an existing Starlette app without including its lifespan; calling the ASGI app in tests without a lifespan context.

Related errors


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