{"record":{"id":"6af5b89058244017","repo":"D4Vinci/Scrapling","slug":"this-fetchersession-instance-already-has-an-active-6af5b8","errorCode":null,"errorMessage":"This FetcherSession instance already has an active asynchronous session.","messagePattern":"This FetcherSession instance already has an active asynchronous session\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/static.py","lineNumber":419,"sourceCode":"        :return: A `Response` object.\n        \"\"\"\n        # Careful of sending a body in a DELETE request, it might cause some websites to reject the request as per https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5,\n        # But some websites accept it, it depends on the implementation used.\n        stealthy_headers = kwargs.pop(\"stealthy_headers\", None)\n        return self._make_request(\"DELETE\", stealth=stealthy_headers, url=url, **kwargs)\n\n\nclass _ASyncSessionLogic(_ConfigurationLogic):\n    __slots__ = (\"_async_curl_session\",)\n\n    def __init__(self, **kwargs: Unpack[RequestsSession]):\n        super().__init__(**kwargs)\n        self._async_curl_session: Optional[AsyncCurlSession] = None\n\n    async def __aenter__(self):  # pragma: no cover\n        \"\"\"Creates and returns a new asynchronous Session.\"\"\"\n        if self._is_alive:\n            raise RuntimeError(\"This FetcherSession instance already has an active asynchronous session.\")\n\n        self._async_curl_session = AsyncCurlSession()\n        self._is_alive = True\n        return self\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"Closes the active asynchronous session managed by this instance, if any.\"\"\"\n        # For type checking (not accessed error)\n        _ = (\n            exc_type,\n            exc_val,\n            exc_tb,\n        )\n        if self._async_curl_session:\n            await self._async_curl_session.close()\n            self._async_curl_session = None\n\n        self._is_alive = False","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/static.py#L401-L437","documentation":"Raised by _ASyncSessionLogic.__aenter__ when the same asynchronous session instance is entered twice. The class supports exactly one live AsyncCurlSession per instance (tracked via _is_alive), so a second __aenter__ before __aexit__ is rejected. This mirrors the synchronous guard and protects the single curl handle from concurrent reuse.","triggerScenarios":"'async with session:' nested inside another 'async with session:' on the same instance; awaiting session.__aenter__() twice manually; a shared AsyncFetcherSession entered concurrently by two tasks (e.g., asyncio.gather over a helper that opens the session).","commonSituations":"Refactoring a sync scraper that shared one session object across functions into async, where each coroutine opens the shared session; re-entering a session inside a retry wrapper that wraps the call in 'async with'.","solutions":["Enter the session once at the top level and pass it to coroutines that only call .get/.fetch, not __aenter__.","Use a separate AsyncFetcherSession instance per concurrent task/context.","For session-less usage, use AsyncFetcherClient, which builds one-off AsyncCurlSessions per request."],"exampleFix":"# before\nasync def fetch_all(session, urls):\n    async with session:  # second task hits RuntimeError\n        ...\n\n# after\nasync def fetch_all(session, urls):\n    return await asyncio.gather(*(session.get(u) for u in urls))\n\nasync with AsyncFetcherSession() as session:\n    await fetch_all(session, urls)","handlingStrategy":"validation","validationCode":"async def open_once(session, coro_factory):\n    if getattr(session, '_is_alive', False):\n        return await coro_factory()  # already open: just use it\n    async with session:\n        return await coro_factory()","typeGuard":"def is_async_session_open(session) -> bool:\n    return getattr(session, '_is_alive', False) is True","tryCatchPattern":"try:\n    async with session:\n        await session.get(url)\nexcept RuntimeError as e:\n    if 'already has an active' in str(e):\n        await session.get(url)  # reuse the already-open session\n    else:\n        raise","preventionTips":["Enter shared sessions once at the top-level coroutine; workers only call .get/.fetch.","Give each concurrent task its own AsyncFetcherSession when isolation matters.","Use AsyncFetcherClient for one-off requests."],"tags":["session-lifecycle","static-fetcher","context-manager","async"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}