PrefectHQ/fastmcp · error · RuntimeError
{new_error_message}\nOriginal error: {e}
Error message
{new_error_message}\nOriginal error: {e} What it means
When a RuntimeError other than the 'task group not initialized' sentinel escapes the ASGI wrapper, FastMCP inspects it and, if it matches its known-cause pattern, re-raises a new RuntimeError that leads with guidance (return the app via fastmcp_instance.http_app(), see the ASGI docs) and appends '\nOriginal error: <e>' for full context.
Source
Thrown at fastmcp_slim/fastmcp/server/http.py:110
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"
)
# Raise a new RuntimeError that includes the original error's message
# for full context, but leads with the more helpful guidance.
raise RuntimeError(f"{new_error_message}\\nOriginal error: {e}") from e
else:
# Re-raise other RuntimeErrors if they don't match the specific message
raise
def _normalize_host(host: str) -> str:
host = host.strip().lower()
if not host:
return ""
if host.startswith("["):
end = host.find("]")
if end == -1:
return host
return host[1:end]
if host.count(":") == 1:
return host.rsplit(":", 1)[0]View on GitHub (pinned to 1f02114297)
Solutions
- Serve the app returned by fastmcp_instance.http_app() rather than constructing/mounting internals yourself.
- Follow the ASGI integration docs (https://gofastmcp.com/deployment/asgi) for correct mounting and lifespan wiring.
- Ensure the session manager's run() executes via the app lifespan before serving requests.
- Read the 'Original error' portion of the message to find the underlying cause and fix it.
Example fix
// before
app = FastMCP("x")
starlette_app.mount("/mcp", app._streamable_http_app_raw())
// after
http_app = app.http_app(path="/mcp")
starlette_app.mount("/", http_app) # lifespan included Defensive patterns
Strategy: try-catch
Validate before calling
# verify the app came from http_app() assert hasattr(fastmcp_instance, "http_app") asgi = fastmcp_instance.http_app(path="/mcp")
Try / catch
try:
await asgi_app(scope, receive, send)
except RuntimeError as e:
if "fastmcp_instance.http_app" in str(e):
logger.error("%s | underlying: %s", e, e.__cause__)
raise Prevention
- Always serve the object returned by fastmcp_instance.http_app()
- Follow the ASGI integration docs for custom mounts
- Keep the lifespan that starts the session manager wired into the server
When it happens
Trigger: A RuntimeError raised during request handling in the StreamableHTTP session manager that matches FastMCP's known misconfiguration pattern — typically running the ASGI app incorrectly (e.g. the session manager not running due to missing lifespan).
Common situations: Mounting the streamable-http app without its lifespan; wrapping the ASGI callable manually instead of using http_app(); deployment misconfigurations where the session manager task group is not started.
Related errors
- Task group is not initialized. Make sure to use run().
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/2f89138dd6e874ce.
Report an issue: GitHub.