{"record":{"id":"05008e0925386581","repo":"D4Vinci/Scrapling","slug":"browser-not-initialized-for-proxy-rotation-mode","errorCode":null,"errorMessage":"Browser not initialized for proxy rotation mode","messagePattern":"Browser not initialized for proxy rotation mode","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/_browsers/_base.py","lineNumber":195,"sourceCode":"            ):\n                xhr_container.append(finished_response)\n\n        return handle_response\n\n    @contextmanager\n    def _page_generator(\n        self,\n        timeout: int | float,\n        extra_headers: Optional[Dict[str, str]],\n        disable_resources: bool,\n        proxy: Optional[ProxyType] = None,\n        blocked_domains: Optional[Set[str]] = None,\n    ) -> Generator[\"PageInfo[Page]\", None, None]:\n        \"\"\"Acquire a page - either from persistent context or fresh context with proxy.\"\"\"\n        if proxy:\n            # Rotation mode: create fresh context with the provided proxy\n            if not self.browser:  # pragma: no cover\n                raise RuntimeError(\"Browser not initialized for proxy rotation mode\")\n            context_options = self._build_context_with_proxy(proxy)\n            context: BrowserContext = self.browser.new_context(**context_options)\n\n            page_info = None\n            try:\n                context = self._initialize_context(self._config, context)\n                page_info = self._get_page(timeout, extra_headers, disable_resources, blocked_domains, context=context)\n                yield page_info\n            finally:\n                if page_info is not None and page_info in self.page_pool.pages:\n                    self.page_pool.pages.remove(page_info)\n                context.close()\n        else:\n            # Standard mode: use PagePool with persistent context\n            page_info = self._get_page(timeout, extra_headers, disable_resources, blocked_domains)\n            try:\n                yield page_info\n            finally:","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/_browsers/_base.py#L177-L213","documentation":"Raised in the sync `_page_generator` of the browser engine base (scrapling/engines/_browsers/_base.py:195). In proxy-rotation mode the engine must create a fresh browser context per proxy via `self.browser.new_context(...)`; if `self.browser` is falsy at that point the generator raises RuntimeError. This is marked `no cover` because it indicates the session's internal startup invariant was broken — a live session always has a browser when a proxy is supplied.","triggerScenarios":"Calling `fetch(url, proxy=...)` or configuring `proxy_rotator` on a `DynamicSession`/`StealthySession` whose `__enter__`/startup did not run or whose browser was already torn down (used after `__exit__`, or startup failed silently); also reachable by poking internals (`session.browser = None`) before a proxied fetch.","commonSituations":"Using the session outside its context manager (`session = DynamicSession(...); session.fetch(url, proxy=p)` without `with`); storing the session and fetching after `__exit__` closed the browser; a previous startup exception that left `_is_alive` inconsistent.","solutions":["Always use the session as a context manager and fetch inside the `with` block: `with DynamicSession(...) as s: s.fetch(url, proxy=proxy)`.","Check `session._is_alive` before fetching if you keep long-lived session references.","Verify startup succeeded — wrap `__enter__` in try/except and don't reuse the session after failure.","If you must manage lifecycle manually, call the session's start method before any proxied fetch."],"exampleFix":"# before\nsession = DynamicSession(proxy_rotator=rotator)\nresp = session.fetch(url)  # browser never started\n\n# after\nwith DynamicSession(proxy_rotator=rotator) as session:\n    resp = session.fetch(url)","handlingStrategy":"type-guard","validationCode":"from scrapling.engines._browsers._controllers import DynamicSession\n\ndef session_ready(session: DynamicSession) -> bool:\n    return bool(getattr(session, \"_is_alive\", False)) and bool(getattr(session, \"browser\", None))","typeGuard":"def can_fetch_with_proxy(session) -> bool:\n    \"\"\"True only inside the context manager with a live browser.\"\"\"\n    return bool(getattr(session, 'browser', None)) and bool(getattr(session, '_is_alive', False))","tryCatchPattern":"try:\n    resp = session.fetch(url, proxy=p)\nexcept RuntimeError as e:\n    if 'Browser not initialized' in str(e):\n        raise RuntimeError('fetch called outside session context') from e\n    raise","preventionTips":["Always fetch inside `with DynamicSession(...) as session:`.","Never reuse a session after its context manager exits.","Treat startup failures as fatal for that session object; create a new one."],"tags":["browser","proxy","lifecycle","playwright","scrapling"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}