{"record":{"id":"44176ba1c546134a","repo":"D4Vinci/Scrapling","slug":"session-session-id-is-no-longer-alive-open-a","errorCode":null,"errorMessage":"Session '{session_id}' is no longer alive. Open a new session.","messagePattern":"Session '(.+?)' is no longer alive\\. Open a new session\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapling/core/ai.py","lineNumber":167,"sourceCode":"        :param auth_token: Optional shared token that clients must send as `Authorization: Bearer <token>`.\n            If omitted, the SCRAPLING_MCP_AUTH_TOKEN environment variable is used when set. It only applies\n            to the streamable-http transport.\n        \"\"\"\n        self._sessions: Dict[str, _SessionEntry] = {}\n        self._executable_path = executable_path or environ.get(MCP_EXECUTABLE_PATH_ENV) or None\n        self._auth_token = auth_token or environ.get(MCP_AUTH_TOKEN_ENV) or None\n\n    def _resolve_executable_path(self, executable_path: Optional[str]) -> Optional[str]:\n        \"\"\"Return a per-call executable path or the server-wide default.\"\"\"\n        return executable_path or self._executable_path\n\n    def _get_session(self, session_id: str, expected_type: Optional[SessionType]) -> _SessionEntry:\n        \"\"\"Look up a session by ID, optionally validating its type. Pass `None` to skip the type check.\"\"\"\n        entry = self._sessions.get(session_id)\n        if entry is None:\n            raise ValueError(f\"Session '{session_id}' not found. Use list_sessions to see active sessions.\")\n        if not entry.session._is_alive:\n            raise ValueError(f\"Session '{session_id}' is no longer alive. Open a new session.\")\n        if expected_type is not None and entry.session_type != expected_type:\n            raise ValueError(\n                f\"Session '{session_id}' is a '{entry.session_type}' session, but this tool requires a \"\n                f\"'{expected_type}' session. Use the matching fetch tool for your session type.\"\n            )\n        return entry\n\n    async def open_session(\n        self,\n        session_type: SessionType,\n        session_id: Optional[str] = None,\n        headless: bool = True,\n        google_search: bool = True,\n        real_chrome: bool = False,\n        wait: int | float = 0,\n        proxy: Optional[str | Dict[str, str]] = None,\n        timezone_id: str | None = None,\n        locale: str | None = None,","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/core/ai.py#L149-L185","documentation":"A session can exist in the registry but have a dead underlying browser (the session object's _is_alive flag is False) — typically because the Playwright browser/context crashed, was closed externally, or the page it was tracking closed. _get_session refuses to operate on it and tells the caller to reopen. The entry stays in the dict, which is why this is distinct from 'not found'.","triggerScenarios":"The browser process for the session crashed (OOM, killed, display server failure in headless containers), or the session was closed at the Playwright level while the MCP entry remained. The next fetch/screenshot on that session_id raises this.","commonSituations":"Long-lived agent sessions in CI containers where Chrome gets OOM-killed or sandbox restrictions kill the browser, stale sessions left after exceptions, or docker images missing shared libraries causing delayed browser death.","solutions":["Close the dead entry: close_session(session_id) to free the registry slot, then open_session again","Check container/browser health: memory limits, --no-sandbox requirements, missing libs, and Docker seccomp settings for Chrome","Structure agent code to reopen transparently when this error is raised"],"exampleFix":"# before\nawait fetch_with_session(session_id=sid, url=...)  # ValueError: no longer alive\n\n# after\ntry:\n    await fetch_with_session(session_id=sid, url=url)\nexcept ValueError:\n    await close_session(sid)\n    sid = (await open_session(session_type='dynamic')).session_id\n    await fetch_with_session(session_id=sid, url=url)","handlingStrategy":"fallback","validationCode":"infos = {s.session_id for s in await list_sessions()}\nif sid not in infos:\n    sid = (await open_session(session_type='dynamic')).session_id","typeGuard":null,"tryCatchPattern":"try:\n    await fetch_with_session(session_id=sid, url=url)\nexcept ValueError as e:\n    if 'no longer alive' in str(e):\n        await close_session(sid)  # free the dead entry (ignore 'not found')\n        sid = (await open_session(session_type='dynamic')).session_id\n        await fetch_with_session(session_id=sid, url=url)\n    else:\n        raise","preventionTips":["Give browser containers enough memory and proper sandbox flags so Chrome does not die mid-session","Periodically validate long-lived sessions with list_sessions before use","Design agent flows to reopen sessions idempotently on lifecycle errors"],"tags":["ai","mcp","session","browser","lifecycle"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}