D4Vinci/Scrapling · error · RuntimeError
Context manager has been closed
Error message
Context manager has been closed
What it means
Raised by StealthySession.fetch() when the session's `_is_alive` flag is False, i.e. the underlying browser context was never started or has already been closed. fetch() can only run while the session context manager is open.
Source
Thrown at scrapling/engines/_browsers/_stealth.py:210
:param page_setup: A function that takes the `page` object, runs before navigation. Use it to register event listeners or routes that must be set up before the page loads.
:param extra_headers: A dictionary of extra headers to add to the request. _The referer set by `google_search` takes priority over the referer set here if used together._
:param disable_resources: Drop requests for unnecessary resources for a speed boost.
Requests dropped are of type `font`, `image`, `media`, `beacon`, `object`, `imageset`, `texttrack`, `websocket`, `csp_report`, and `stylesheet`.
:param blocked_domains: A set of domain names to block requests to. Subdomains are also matched (e.g., ``"example.com"`` blocks ``"sub.example.com"`` too).
:param wait_selector: Wait for a specific CSS selector to be in a specific state.
:param wait_selector_state: The state to wait for the selector given with `wait_selector`. The default state is `attached`.
:param network_idle: Wait for the page until there are no network connections for at least 500 ms.
:param load_dom: Enabled by default, wait for all JavaScript on page(s) to fully load and execute.
:param solve_cloudflare: Solves all types of the Cloudflare's Turnstile/Interstitial challenges before returning the response to you.
:param selector_config: The arguments that will be passed in the end while creating the final Selector's class.
:param proxy: Static proxy to override rotator and session proxy. A new browser context will be created and used with it.
:return: A `Response` object.
"""
static_proxy = kwargs.pop("proxy", None)
params = _validate(kwargs, self, StealthConfig)
if not self._is_alive: # pragma: no cover
raise RuntimeError("Context manager has been closed")
request_headers_keys = {h.lower() for h in params.extra_headers.keys()} if params.extra_headers else set()
referer = (
"https://www.google.com/" if (params.google_search and "referer" not in request_headers_keys) else None
)
for attempt in range(self._config.retries):
proxy: Optional[ProxyType] = None
if self._config.proxy_rotator and static_proxy is None:
proxy = self._config.proxy_rotator.get_proxy()
else:
proxy = static_proxy
with self._page_generator(
params.timeout, params.extra_headers, params.disable_resources, proxy, params.blocked_domains
) as page_info:
final_response: List = [None]
xhr_captured: List = []View on GitHub (pinned to 5d213a2d47)
Solutions
- Move all fetch() calls inside the `with StealthySession(...) as session:` block
- Ensure start() succeeded before fetching and that no prior error closed the session
- For long-lived usage, keep the context manager open for the whole scraping run and close it in a finally block
Example fix
// before
with StealthySession() as s:
pass
resp = s.fetch('https://example.com') # RuntimeError
// after
with StealthySession() as s:
resp = s.fetch('https://example.com') Defensive patterns
Strategy: validation
Validate before calling
if not session._is_alive:
raise RuntimeError('session closed; reopen before fetching')
resp = session.fetch(url) Type guard
def can_fetch(s) -> bool:
return bool(getattr(s, '_is_alive', False)) Try / catch
try:
resp = session.fetch(url)
except RuntimeError as e:
if 'closed' in str(e):
# reopen a new session and retry once
with StealthySession() as s2:
resp = s2.fetch(url)
else:
raise Prevention
- Keep every fetch() inside the `with` block; structure code so the session outlives its fetches
- If managing lifecycle manually, guard fetch with `if not session._is_alive: restart`
- Gather/cancel background tasks before the context manager exits
When it happens
Trigger: Calling fetch() after the `with StealthySession(...)` block exited, calling fetch() before start(), or after stop()/an internal crash tore down the browser.
Common situations: Storing the session and calling fetch() later in a different function/scope after the context manager closed, or an earlier exception in __exit__ leaving the session dead.
Related errors
- Session has been already started
- This FetcherSession instance already has an active synchrono
- No active session available.
- Cannot exit invalid session
- Failed to get response for {url}
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/277139240ba0783d.
Report an issue: GitHub.