From a6d21b5b43096d9c77770fa0ba6fdaaddff32231 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 16:14:44 +0000 Subject: [PATCH] Fix Playwright page crash error with retry mechanism - Add import for PlaywrightError from playwright.async_api - Implement retry logic (max 2 attempts) for page crashes - When page crashes, close the crashed page and create a new one from context - Use current_page variable to track the active page instance - Initialize page_request and status to prevent undefined variable errors - Add proper error logging for page crash scenarios This fix resolves the "Page.goto: Page crashed" error by gracefully handling page crashes and retrying the operation with a fresh page. --- src/endpoints.py | 95 +++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/src/endpoints.py b/src/endpoints.py index 5be2143..400262b 100644 --- a/src/endpoints.py +++ b/src/endpoints.py @@ -6,6 +6,7 @@ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import RedirectResponse +from playwright.async_api import Error as PlaywrightError from playwright_captcha import CaptchaType from src.consts import CHALLENGE_TITLES @@ -57,50 +58,78 @@ async def read_item(request: LinkRequest, dep: CamoufoxDep) -> LinkResponse: timer = TimeoutTimer(duration=request.max_timeout) request.url = request.url.replace('"', "").strip() - try: - page_request = await dep.page.goto( - request.url, timeout=timer.remaining() * 1000 - ) - status = page_request.status if page_request else HTTPStatus.OK - await dep.page.wait_for_load_state( - state="domcontentloaded", timeout=timer.remaining() * 1000 - ) - await dep.page.wait_for_load_state( - "networkidle", timeout=timer.remaining() * 1000 - ) - if await dep.page.title() in CHALLENGE_TITLES: - logger.info("Challenge detected, attempting to solve...") - # Solve the captcha - await wait_for( - dep.solver.solve_captcha( # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] - captcha_container=dep.page, - captcha_type=CaptchaType.CLOUDFLARE_INTERSTITIAL, - wait_checkbox_attempts=1, - wait_checkbox_delay=0.5, - ), - timeout=timer.remaining(), + # Track retry attempts for page crashes + max_retries = 2 + current_page = dep.page + page_request = None + status = HTTPStatus.OK + + for attempt in range(max_retries): + try: + page_request = await current_page.goto( + request.url, timeout=timer.remaining() * 1000 ) - status = HTTPStatus.OK - logger.debug("Challenge solved successfully.") - except TimeoutError as e: - logger.error("Timed out while solving the challenge") - raise HTTPException( - status_code=408, - detail="Timed out while solving the challenge", - ) from e + status = page_request.status if page_request else HTTPStatus.OK + await current_page.wait_for_load_state( + state="domcontentloaded", timeout=timer.remaining() * 1000 + ) + await current_page.wait_for_load_state( + "networkidle", timeout=timer.remaining() * 1000 + ) + + if await current_page.title() in CHALLENGE_TITLES: + logger.info("Challenge detected, attempting to solve...") + # Solve the captcha + await wait_for( + dep.solver.solve_captcha( # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] + captcha_container=current_page, + captcha_type=CaptchaType.CLOUDFLARE_INTERSTITIAL, + wait_checkbox_attempts=1, + wait_checkbox_delay=0.5, + ), + timeout=timer.remaining(), + ) + status = HTTPStatus.OK + logger.debug("Challenge solved successfully.") + break # Success, exit retry loop + except PlaywrightError as e: + if "Page crashed" in str(e) and attempt < max_retries - 1: + logger.warning(f"Page crashed on attempt {attempt + 1}, recreating page and retrying...") + try: + # Close the crashed page + await current_page.close() + except Exception as close_error: + logger.debug(f"Error closing crashed page: {close_error}") + + # Create a new page from the existing context + current_page = await dep.context.new_page() + continue # Retry with the new page + else: + # Either not a page crash or out of retries + logger.error(f"Playwright error: {e}") + raise HTTPException( + status_code=500, + detail=f"Browser error: {str(e)}", + ) from e + except TimeoutError as e: + logger.error("Timed out while solving the challenge") + raise HTTPException( + status_code=408, + detail="Timed out while solving the challenge", + ) from e cookies = await dep.context.cookies() return LinkResponse( message="Success", solution=Solution( - user_agent=await dep.page.evaluate("navigator.userAgent"), - url=dep.page.url, + user_agent=await current_page.evaluate("navigator.userAgent"), + url=current_page.url, status=status, cookies=cookies, headers=page_request.headers if page_request else {}, - response=await dep.page.content(), + response=await current_page.content(), ), start_timestamp=start_time, )