Handle upstream errors; recycle the browser only for explicit reCAPTCHA evaluation failures.
(self, browser_ref: Optional[Union[int, str]] = None, error_reason: Optional[str] = None)
| 2986 | await self._release_slot_reservation(browser_id) |
| 2987 | |
| 2988 | async def report_error(self, browser_ref: Optional[Union[int, str]] = None, error_reason: Optional[str] = None): |
| 2989 | """Handle upstream errors; recycle the browser only for explicit reCAPTCHA evaluation failures.""" |
| 2990 | browser_id, _ = self._parse_browser_ref(browser_ref) |
| 2991 | |
| 2992 | async with self._browsers_lock: |
| 2993 | browser = self._browsers.get(browser_id) if browser_id is not None else None |
| 2994 | error_lower = (error_reason or "").lower() |
| 2995 | has_recaptcha = "recaptcha" in error_lower |
| 2996 | should_recycle = has_recaptcha and ( |
| 2997 | "evaluation failed" in error_lower |
| 2998 | or "verification failed" in error_lower or "验证失败" in (error_reason or "") |
| 2999 | or "failed" in error_lower |
| 3000 | ) |
| 3001 | if should_recycle: |
| 3002 | self._stats["api_403"] += 1 |
| 3003 | if browser_id is not None: |
| 3004 | debug_logger.log_info( |
| 3005 | f"[BrowserCaptcha] browser {browser_id} failure reported, reason={error_reason or 'unknown'}, recycle={should_recycle}" |
| 3006 | ) |
| 3007 | |
| 3008 | if browser and should_recycle: |
| 3009 | try: |
| 3010 | await browser.recycle_browser( |
| 3011 | reason=error_reason or "recaptcha_evaluation_failed", |
| 3012 | rotate_profile=True, |
| 3013 | ) |
| 3014 | except Exception as e: |
| 3015 | debug_logger.log_warning(f"[BrowserCaptcha] browser {browser_id} recycle failed: {e}") |
| 3016 | |
| 3017 | async def report_request_finished(self, browser_ref: Optional[Union[int, str]] = None): |
| 3018 | """上层通知本次请求已完成;browser 模式仅保留常驻浏览器,不在成功后主动关闭。""" |
no test coverage detected