dgtlmoon/changedetection.io · error · EmptyReply
EmptyReply
Error message
EmptyReply
What it means
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.
Source
Thrown at changedetectionio/content_fetchers/puppeteer.py:429
response = None
attempt=0
while not response:
logger.debug(f"Attempting page fetch {url} attempt {attempt}")
asyncio.create_task(handle_frame_navigation())
response = await self.page.goto(url, timeout=0)
await asyncio.sleep(1 + extra_wait)
# Check if page still exists before sending command
if self.page and hasattr(self.page, '_client'):
await self.page._client.send('Page.stopLoading')
if response:
break
if not response:
logger.warning("Page did not fetch! trying again!")
if response is None and attempt>=2:
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}")
raise EmptyReply(url=url, status_code=None)
attempt+=1
self.headers = response.headers
try:
if self.webdriver_js_execute_code is not None and len(self.webdriver_js_execute_code):
await self.page.evaluate(self.webdriver_js_execute_code)
except Exception as e:
logger.warning("Got exception when running evaluate on custom JS code")
logger.error(str(e))
# This can be ok, we will try to grab what we could retrieve
raise PageUnloadable(url=url, status_code=None, message=str(e))
try:
self.status_code = response.status
except Exception as e:
# https://github.com/dgtlmoon/changedetection.io/discussions/2122#discussioncomment-8241962
logger.critical(f"Response from the browser/Playwright did not have a status_code! Response follows.")View on GitHub (pinned to 5d9c7c6da7)
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
Defensive patterns
Strategy: retry
Try / catch
try:
fetcher.fetch_page()
except EmptyReply as e:
if e.status_code is None:
retry_with_backoff(max_attempts=3)
else:
mark_watch_failed(e.status_code) Prevention
- Don't watch direct download URLs
- Keep browser service resources adequate
- Migrate puppeteer watches to playwright
When it happens
Trigger: Repeated navigation attempts where pyppeteer's response object is None — downloads, about:blank navigation, protocol mismatch, or crashed targets.
Common situations: URL starts a download; pyppeteer protocol incompatibility with newer Chrome; browser service instability.
Related errors
- Non200ErrorCodeReceived
- Error while trying to connect the browser, Code {e.status_co
- Error connecting to the browser, check your browser connecti
- Error connecting to the browser - Exception '{str(e)}'
- Content Fetcher > xPath scraper failed. Please report this U
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/69ec80d718a9aff4.
Report an issue: GitHub.