D4Vinci/Scrapling · error · RuntimeError

No active session available.

Error message

No active session available.

What it means

Raised inside _SyncSessionLogic.fetch when no curl session is available to send the request with. The method first tries self._curl_session (set by __enter__), then falls back to creating a one-off CurlSession, but only when _curl_session is the _NO_SESSION sentinel and __enter__ has been nullified — i.e., only for the FetcherClient subclass. For a plain FetcherSession used outside its 'with' block, _curl_session stays None and falsy, so this RuntimeError fires. It is marked 'pragma: no cover' because it guards the misuse path.

Source

Thrown at scrapling/engines/static.py:244

        Perform an HTTP request using the configured session.
        """
        stealth = self._stealth if stealth is None else stealth

        selector_config = self._get_param(kwargs, "selector_config", self.selector_config) or self.selector_config
        max_retries = self._get_param(kwargs, "retries", self._default_retries)
        retry_delay = self._get_param(kwargs, "retry_delay", self._default_retry_delay)
        static_proxy = kwargs.pop("proxy", None)

        session = self._curl_session
        one_off_request = False
        if session is _NO_SESSION and self.__enter__ is None:
            # For usage inside FetcherClient
            # It turns out `curl_cffi` caches impersonation state, so if you turned it off, then on then off, it won't be off on the last time.
            session = CurlSession()
            one_off_request = True

        if not session:
            raise RuntimeError("No active session available.")  # pragma: no cover

        try:
            for attempt in range(max_retries):
                proxy: Optional[ProxyType]
                if self._proxy_rotator and static_proxy is None:
                    proxy = self._proxy_rotator.get_proxy()
                else:
                    proxy = static_proxy or self._default_proxy

                request_args = self._merge_request_args(stealth=stealth, proxy=proxy, **kwargs)
                try:
                    response = session.request(method, **request_args)
                    assert response is not None
                    result = ResponseFactory.from_http_request(response, selector_config, meta={"proxy": proxy})
                    return result
                except CurlError as e:  # pragma: no cover
                    if attempt < max_retries - 1:
                        # Now if the rotator is enabled, we will try again with the new proxy

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. Wrap usage in a context manager: 'with FetcherSession(...) as session: session.get(url)'.
  2. If you don't want a session lifecycle, use FetcherClient (or AsyncFetcher's one-shot functions) which create one-off sessions automatically.
  3. Ensure no code path calls request methods after the 'with' block ends (e.g., async callbacks or background threads outliving the block).

Example fix

# before
session = FetcherSession(stealth=True)
resp = session.get('https://example.com')  # RuntimeError

# after
with FetcherSession(stealth=True) as session:
    resp = session.get('https://example.com')
Defensive patterns

Strategy: validation

Validate before calling

def ensure_open(session):
    # A usable session is either inside its context or a FetcherClient (one-off mode)
    if not getattr(session, '_is_alive', False) and getattr(session, '_curl_session', None) is None:
        raise RuntimeError('FetcherSession must be used inside "with"; or switch to FetcherClient')

# usage
with FetcherSession() as session:
    ensure_open(session)
    session.get(url)

Type guard

def has_active_sync_session(session) -> bool:
    return getattr(session, '_is_alive', False) is True and getattr(session, '_curl_session', None) is not None

Try / catch

try:
    resp = session.get(url)
except RuntimeError as e:
    if 'No active session' in str(e):
        with FetcherSession(**cfg) as s:
            resp = s.get(url)
    else:
        raise

Prevention

When it happens

Trigger: Creating FetcherSession(...) and calling session.get(url) / .fetch(...) without entering the 'with' block first; calling request methods after __exit__ has already closed and cleared the session; calling methods on a _SyncSessionLogic whose __enter__ raised midway so _curl_session was never assigned.

Common situations: Treating FetcherSession like the older stateless API (pre-context-manager versions allowed direct .get()); refactoring code from FetcherClient to FetcherSession and forgetting the 'with'; calling fetch from a helper that runs after the context block exited.

Related errors


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/07d53ddaeb758428. Report an issue: GitHub.