{"record":{"id":"a5e0321696fa2a4b","repo":"D4Vinci/Scrapling","slug":"request-failed","errorCode":null,"errorMessage":"Request failed","messagePattern":"Request failed","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scrapling/engines/_browsers/_controllers.py","lineNumber":212,"sourceCode":"                    return response\n\n                except Exception as e:\n                    page_info.mark_error()\n                    if attempt < self._config.retries - 1:\n                        if is_proxy_error(e):\n                            log.warning(\n                                f\"Proxy '{proxy}' failed (attempt {attempt + 1}) | Retrying in {self._config.retry_delay}s...\"\n                            )\n                        else:\n                            log.warning(\n                                f\"Attempt {attempt + 1} failed: {e}. Retrying in {self._config.retry_delay}s...\"\n                            )\n                        time_sleep(self._config.retry_delay)\n                    else:\n                        log.error(f\"Failed after {self._config.retries} attempts: {e}\")\n                        raise\n\n        raise RuntimeError(\"Request failed\")  # pragma: no cover\n\n\nclass AsyncDynamicSession(AsyncSession, DynamicSessionMixin):\n    \"\"\"An async Browser session manager with page pooling, it's using a persistent browser Context by default with a temporary user profile directory.\"\"\"\n\n    __slots__ = (\n        \"_config\",\n        \"_context_options\",\n        \"_browser_options\",\n        \"_user_data_dir\",\n        \"_headers_keys\",\n    )\n\n    def __init__(self, **kwargs: Unpack[PlaywrightSession]):\n        \"\"\"A Browser session manager with page pooling\n\n        :param headless: Run the browser in headless/hidden (default), or headful/visible mode.\n        :param disable_resources: Drop requests for unnecessary resources for a speed boost.","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/engines/_browsers/_controllers.py#L194-L230","documentation":"End-of-function raise in sync `DynamicSession.fetch` (scrapling/engines/_browsers/_controllers.py:212), reached only when the retry loop finishes without returning and without re-raising — practically it fires when `self._config.retries` is 0 and the single attempt failed without raising (or the loop exits through a path that neither returns nor raises). It is the 'impossible state' backstop, marked `# pragma: no cover`; in almost all real failures the last exception is re-raised with `raise` inside the loop instead.","triggerScenarios":"Constructing a session with `retries=0` and hitting a navigation that returns no response without another exception; code that catches exceptions inside `page_action`/`page_setup` swallowing the failure so the loop simply exhausts; theoretically unreachable when `retries >= 1` and the last attempt raises.","commonSituations":"Setting `retries=0` in `PlaywrightConfig` to disable retries and then seeing this generic message instead of the underlying cause; user callbacks that swallow exceptions making the loop fall through.","solutions":["Set `retries >= 1` (default) so the final attempt re-raises the original, more informative exception instead of falling through.","Don't swallow exceptions in `page_action`/`page_setup` — let them propagate so the retry logic sees the real error.","Log `params` and the URL when this fires to reconstruct which attempt path fell through.","Report as a bug if it reproduces with default `retries`, since the loop should always return or re-raise."],"exampleFix":"# before\nsession = DynamicSession(PlaywrightConfig(retries=0))\nresp = session.fetch(url)  # falls through -> generic 'Request failed'\n\n# after\nsession = DynamicSession(PlaywrightConfig(retries=2, retry_delay=1))\nresp = session.fetch(url)  # last attempt re-raises the real exception","handlingStrategy":"retry","validationCode":"def retries_sane(config) -> bool:\n    return getattr(config, 'retries', 0) >= 1","typeGuard":null,"tryCatchPattern":"try:\n    resp = session.fetch(url)\nexcept RuntimeError as e:\n    if str(e) == 'Request failed':\n        log.error('fetch fell through retry loop; check retries>=1 and page_action exception handling')\n    raise","preventionTips":["Never set PlaywrightConfig(retries=0) unless you handle all failures yourself.","Don't swallow exceptions inside page_action/page_setup.","Log the final attempt's exception rather than relying on this generic message."],"tags":["browser","retries","fallback-unreachable","scrapling"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}