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
- Run the app through its lifespan: use fastmcp http_app() with a server that supports lifespan (uvicorn app, not just the raw ASGI callable).
- In a custom Starlette app, use its .router/lifespan context so FastMCP's lifespan (which calls session_manager.run()) executes before requests.
- Enable lifespan in the deployment (e.g. remove lifespan='off' from uvicorn/gunicorn config).
- 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
- Never disable lifespan in uvicorn/gunicorn when serving FastMCP http apps
- Include the FastMCP lifespan when mounting into a custom Starlette app
- Use asgi_lifespan.LifespanManager in tests around the ASGI app
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
- {new_error_message}\nOriginal error: {e}
- Cannot compose Lifespan with {type(other).__name__}. Use @li
- StreamingASGITransport requires an async request stream; got
- Unexpected ASGI message type: {message['type']}
- uv is not installed. Please install it with: curl -LsSf http
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/a621d940e83f8c73.
Report an issue: GitHub.