D4Vinci/Scrapling · error · RuntimeError
This FetcherSession instance already has an active asynchron
Error message
This FetcherSession instance already has an active asynchronous session.
What it means
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.
Source
Thrown at scrapling/engines/static.py:419
:return: A `Response` object.
"""
# 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,
# But some websites accept it, it depends on the implementation used.
stealthy_headers = kwargs.pop("stealthy_headers", None)
return self._make_request("DELETE", stealth=stealthy_headers, url=url, **kwargs)
class _ASyncSessionLogic(_ConfigurationLogic):
__slots__ = ("_async_curl_session",)
def __init__(self, **kwargs: Unpack[RequestsSession]):
super().__init__(**kwargs)
self._async_curl_session: Optional[AsyncCurlSession] = None
async def __aenter__(self): # pragma: no cover
"""Creates and returns a new asynchronous Session."""
if self._is_alive:
raise RuntimeError("This FetcherSession instance already has an active asynchronous session.")
self._async_curl_session = AsyncCurlSession()
self._is_alive = True
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Closes the active asynchronous session managed by this instance, if any."""
# For type checking (not accessed error)
_ = (
exc_type,
exc_val,
exc_tb,
)
if self._async_curl_session:
await self._async_curl_session.close()
self._async_curl_session = None
self._is_alive = FalseView on GitHub (pinned to 5d213a2d47)
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.
Example fix
# before
async def fetch_all(session, urls):
async with session: # second task hits RuntimeError
...
# after
async def fetch_all(session, urls):
return await asyncio.gather(*(session.get(u) for u in urls))
async with AsyncFetcherSession() as session:
await fetch_all(session, urls) Defensive patterns
Strategy: validation
Validate before calling
async def open_once(session, coro_factory):
if getattr(session, '_is_alive', False):
return await coro_factory() # already open: just use it
async with session:
return await coro_factory() Type guard
def is_async_session_open(session) -> bool:
return getattr(session, '_is_alive', False) is True Try / catch
try:
async with session:
await session.get(url)
except RuntimeError as e:
if 'already has an active' in str(e):
await session.get(url) # reuse the already-open session
else:
raise Prevention
- 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.
When it happens
Trigger: '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).
Common situations: 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'.
Related errors
- This FetcherSession instance already has an active synchrono
- No active session available.
- Cannot exit invalid session
- Session has been already started
- Context manager has been closed
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/6af5b89058244017.
Report an issue: GitHub.