{"record":{"id":"216ed41390202533","repo":"PrefectHQ/fastmcp","slug":"session-id-is-not-available-because-no-session-exi","errorCode":null,"errorMessage":"session_id is not available because no session exists. This typically means you're outside a request context.","messagePattern":"session_id is not available because no session exists\\. This typically means you're outside a request context\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/context.py","lineNumber":750,"sourceCode":"        from uuid import uuid4\n\n        # Get session from request context or _session (for on_initialize)\n        request_ctx = self.request_context\n        if request_ctx is not None:\n            session = request_ctx.session\n        elif self._session is not None:\n            session = self._session\n        else:\n            # Background task: no live session, but the submitting request's\n            # stable session id was captured in the task snapshot. Use it so\n            # session-scoped state (session_id / get_state / set_state) keeps\n            # working in a worker, keyed to the same client that submitted.\n            from fastmcp.server.dependencies import _background_task_session_id\n\n            task_session_id = _background_task_session_id.get()\n            if task_session_id is not None:\n                return task_session_id\n            raise RuntimeError(\n                \"session_id is not available because no session exists. \"\n                \"This typically means you're outside a request context.\"\n            )\n\n        # In SDK v2 the ServerSession is constructed fresh per request, so the\n        # stable per-client identity lives on the underlying Connection, which\n        # persists for the whole client session. Cache the state prefix on the\n        # connection (its `session_id` for HTTP, its `state` dict otherwise) so\n        # session-scoped state survives across tool calls.\n        connection = getattr(session, \"_connection\", None)\n\n        # Check for a cached prefix on the stable connection (or the session, as\n        # a fallback for on_initialize where only a raw session is available).\n        if connection is not None:\n            cached = connection.state.get(\"_fastmcp_state_prefix\")\n            if cached is not None:\n                return cached\n        session_cached = getattr(session, \"_fastmcp_state_prefix\", None)","sourceCodeStart":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/context.py#L732-L768","documentation":"Context.session_id resolves the stable per-client session identity. If no session exists — you are outside a request context and no background-task session ID was stashed in _background_task_session_id — the property raises RuntimeError. This typically means the code is not running inside any MCP request.","triggerScenarios":"Accessing ctx.session_id outside a request (startup hooks, standalone scripts, tests), or in a background/worker task that did not propagate the session ID via the _background_task_session_id contextvar.","commonSituations":"Scheduled jobs calling tools directly; test harnesses constructing Context objects; worker pools executing tool logic detached from the submitting request without the background-task session propagation FastMCP provides.","solutions":["Only access session_id inside a live request context.","For background work, submit tasks through FastMCP's background-task mechanism, which sets _background_task_session_id from the submitting client.","Capture session_id in the request and pass it explicitly to deferred code.","Guard with a None check on the underlying session/request_context before reading the property."],"exampleFix":"// before\nasync def tool(ctx: Context):\n    asyncio.create_task(worker(ctx))  # session_id lost\n// after\nasync def tool(ctx: Context):\n    sid = ctx.session_id  # capture inside the request\n    asyncio.create_task(worker(sid))","handlingStrategy":"validation","validationCode":"sid = ctx.session_id if ctx.request_context is not None else None\nif sid is None:\n    sid = \"no-session\"","typeGuard":"def has_session(ctx) -> bool:\n    try:\n        return ctx.request_context is not None\n    except Exception:\n        return False","tryCatchPattern":"try:\n    sid = ctx.session_id\nexcept RuntimeError:\n    sid = None  # outside a request context","preventionTips":["Use FastMCP's background-task mechanism so _background_task_session_id propagates to workers.","Capture session_id inside the request and pass it explicitly to deferred code.","Never access ctx.session_id from startup hooks, scheduled jobs, or bare Context instances."],"tags":["python","context","session","runtimeerror","background-tasks"],"backgroundTag":"no-active-request-context","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}