{"record":{"id":"69ec80d718a9aff4","repo":"dgtlmoon/changedetection.io","slug":"emptyreply-69ec80","errorCode":null,"errorMessage":"EmptyReply","messagePattern":"EmptyReply","errorType":"exception","errorClass":"EmptyReply","httpStatus":null,"severity":"error","filePath":"changedetectionio/content_fetchers/puppeteer.py","lineNumber":429,"sourceCode":"\n        response = None\n        attempt=0\n        while not response:\n            logger.debug(f\"Attempting page fetch {url} attempt {attempt}\")\n            asyncio.create_task(handle_frame_navigation())\n            response = await self.page.goto(url, timeout=0)\n            await asyncio.sleep(1 + extra_wait)\n            # Check if page still exists before sending command\n            if self.page and hasattr(self.page, '_client'):\n                await self.page._client.send('Page.stopLoading')\n\n            if response:\n                break\n            if not response:\n                logger.warning(\"Page did not fetch! trying again!\")\n            if response is None and attempt>=2:\n                logger.warning(f\"Content Fetcher > Response object was none (as in, the response from the browser was empty, not just the content) exiting attempt {attempt}\")\n                raise EmptyReply(url=url, status_code=None)\n            attempt+=1\n\n        self.headers = response.headers\n\n        try:\n            if self.webdriver_js_execute_code is not None and len(self.webdriver_js_execute_code):\n                await self.page.evaluate(self.webdriver_js_execute_code)\n        except Exception as e:\n            logger.warning(\"Got exception when running evaluate on custom JS code\")\n            logger.error(str(e))\n            # This can be ok, we will try to grab what we could retrieve\n            raise PageUnloadable(url=url, status_code=None, message=str(e))\n\n        try:\n            self.status_code = response.status\n        except Exception as e:\n            # https://github.com/dgtlmoon/changedetection.io/discussions/2122#discussioncomment-8241962\n            logger.critical(f\"Response from the browser/Playwright did not have a status_code! Response follows.\")","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/content_fetchers/puppeteer.py#L411-L447","documentation":"After retrying page.goto up to 3 attempts, the puppeteer fetcher still received a None response object from the browser, so it raises EmptyReply with status_code None.","triggerScenarios":"Repeated navigation attempts where pyppeteer's response object is None — downloads, about:blank navigation, protocol mismatch, or crashed targets.","commonSituations":"URL starts a download; pyppeteer protocol incompatibility with newer Chrome; browser service instability.","solutions":["Verify the URL returns an HTML document, not a download","Switch fetcher to Playwright (actively maintained)","Check browser service logs for crashes during navigation"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    fetcher.fetch_page()\nexcept EmptyReply as e:\n    if e.status_code is None:\n        retry_with_backoff(max_attempts=3)\n    else:\n        mark_watch_failed(e.status_code)","preventionTips":["Don't watch direct download URLs","Keep browser service resources adequate","Migrate puppeteer watches to playwright"],"tags":["puppeteer","fetcher","empty-response"],"backgroundTag":"empty-response-body","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}