PrefectHQ/fastmcp · error · RuntimeError

session is not available because the MCP session has not bee

Error message

session is not available because the MCP session has not been established yet. Check `context.request_context` for None before accessing this attribute.

What it means

Context.session returns the low-level MCP ServerSession. It prefers the per-request session from request_context, falls back to a stored session (e.g. during on_initialize), and raises RuntimeError when neither exists — i.e. no MCP session has been established for this Context.

Source

Thrown at fastmcp_slim/fastmcp/server/context.py:814

        In request mode: Returns the session from the active request context.
        In background task mode: Returns the session stored at Context creation.

        Raises RuntimeError if no session is available.
        """
        # Background task mode: use the stored session
        if self.is_background_task and self._session is not None:
            return self._session

        # Request mode: use request context
        if self.request_context is not None:
            return self.request_context.session

        # Fallback to stored session (e.g., during on_initialize)
        if self._session is not None:
            return self._session

        raise RuntimeError(
            "session is not available because the MCP session has not been established yet. "
            "Check `context.request_context` for None before accessing this attribute."
        )

    # Convenience methods for common log levels
    async def debug(
        self,
        message: str,
        logger_name: str | None = None,
        extra: Mapping[str, Any] | None = None,
    ) -> None:
        """Send a `DEBUG`-level message to the connected MCP Client.

        Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`."""
        await self.log(
            level="debug",
            message=message,
            logger_name=logger_name,

View on GitHub (pinned to 1f02114297)

Solutions

  1. Call session-dependent code only from within a request handler or after on_initialize has stored a session.
  2. Check ctx.request_context is not None (or wrap in try-except RuntimeError) before accessing ctx.session.
  3. Pass the session explicitly into helpers instead of reading it from Context at arbitrary times.
  4. For on_initialize-era code, rely on the stored-session fallback rather than caching ctx.session yourself.

Example fix

// before
async def helper(ctx: Context):
    await ctx.session.send_log_message(level="info", data="hi")  # may raise
// after
async def helper(ctx: Context):
    if ctx.request_context is not None:
        await ctx.session.send_log_message(level="info", data="hi")
Defensive patterns

Strategy: type-guard

Validate before calling

session = ctx.request_context.session if ctx.request_context is not None else None

Type guard

def session_available(ctx) -> bool:
    return ctx.request_context is not None or getattr(ctx, "_session", None) is not None

Try / catch

try:
    session = ctx.session
except RuntimeError:
    session = None  # no MCP session established yet

Prevention

When it happens

Trigger: Accessing ctx.session in code that runs before a session exists: outside any request, after the request finished (stale Context), or during early lifecycle phases where neither request_context nor the stored _session is set.

Common situations: Direct low-level session.send_* calls from helpers invoked at import time; tests instantiating Context manually; middleware that runs before initialization completes and assumes a session is present.

Related errors


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