D4Vinci/Scrapling · error · RuntimeError
Cannot exit invalid session
Error message
Cannot exit invalid session
What it means
Raised by FetcherSession.__exit__ when there is no active synchronous inner client to close: either self._client is None (nothing was entered, or it was already exited) or _client holds an _ASyncSessionLogic (an async session is open, which must be closed with __aexit__, not __exit__). It prevents silently closing the wrong session type.
Source
Thrown at scrapling/engines/static.py:740
config["selector_config"] = self.selector_config
config["proxy_rotator"] = self._proxy_rotator
self._client = _SyncSessionLogic(**config)
try:
result = self._client.__enter__()
except Exception:
self._client = None
raise
self._is_alive = True
return result
raise RuntimeError("This FetcherSession instance already has an active synchronous session.")
def __exit__(self, exc_type, exc_val, exc_tb):
if self._client is not None and isinstance(self._client, _SyncSessionLogic):
self._client.__exit__(exc_type, exc_val, exc_tb)
self._client = None
self._is_alive = False
return
raise RuntimeError("Cannot exit invalid session")
async def __aenter__(self) -> _ASyncSessionLogic:
"""Creates and returns a new asynchronous Session."""
if self._client is None:
# Use **vars(self) to avoid repeating all parameters
config = {k.replace("_default_", ""): getattr(self, k) for k in self.__slots__ if k.startswith("_default")}
config["stealthy_headers"] = self._stealth
config["selector_config"] = self.selector_config
config["proxy_rotator"] = self._proxy_rotator
self._client = _ASyncSessionLogic(**config)
try:
result = await self._client.__aenter__()
except Exception:
self._client = None
raise
self._is_alive = True
return result
raise RuntimeError("This FetcherSession instance already has an active asynchronous session.")View on GitHub (pinned to 5d213a2d47)
Solutions
- Let the 'with' statement manage exit; never call __exit__ manually for the same block.
- Match protocols: close async sessions only with 'async with' / __aexit__.
- Guard cleanup code with a check that the session is actually alive before exiting.
Example fix
# before
with fs:
...
fs.__exit__(None, None, None) # RuntimeError: already exited by 'with'
# after
with fs:
...
# nothing else needed; 'with' already exited cleanly Defensive patterns
Strategy: validation
Validate before calling
def safe_sync_exit(fs, *exc):
client = getattr(fs, '_client', None)
from scrapling.engines.static import _SyncSessionLogic
if client is not None and isinstance(client, _SyncSessionLogic):
fs.__exit__(*exc)
# otherwise: nothing (or an async session) to close synchronously Type guard
def can_sync_exit(fs) -> bool:
from scrapling.engines.static import _SyncSessionLogic
client = getattr(fs, '_client', None)
return client is not None and isinstance(client, _SyncSessionLogic) Try / catch
try:
fs.__exit__(exc_type, exc_val, exc_tb)
except RuntimeError as e:
if 'Cannot exit invalid session' in str(e):
pass # nothing to close; session already exited or is async
else:
raise Prevention
- Never call __exit__ manually for blocks managed by 'with'.
- Match exit protocol to enter protocol (sync/async).
- In custom wrappers, track entered state and exit exactly once.
When it happens
Trigger: Calling __exit__ twice (double 'with' exit, or manual __exit__ after the context block); calling sync __exit__ when the session was opened with 'async with'; calling __exit__ on a fresh instance that never entered.
Common situations: A try/finally that manually calls session.__exit__() colliding with the context manager's own exit; mixed sync/async code closing the shared FetcherSession with the wrong protocol; cleanup code that runs after an earlier cleanup already reset _client to None.
Related errors
- This FetcherSession instance already has an active synchrono
- No active session available.
- This FetcherSession instance already has an active asynchron
- 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/240a8797cdeb2084.
Report an issue: GitHub.