dgtlmoon/changedetection.io · error · Non200ErrorCodeReceived

Non200ErrorCodeReceived

Error message

Non200ErrorCodeReceived

What it means

Same non-200 guard as the playwright fetcher: puppeteer fetch_page raises Non200ErrorCodeReceived (with full-page screenshot) when status != 200 and ignore_status_codes is off.

Source

Thrown at changedetectionio/content_fetchers/puppeteer.py:460

        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.")
            logger.critical(response)
            raise PageUnloadable(url=url, status_code=None, message=str(e))

        if fetch_favicon:
            try:
                self.favicon_blob = await self.page.evaluate(FAVICON_FETCHER_JS)
            except Exception as e:
                logger.error(f"Error fetching FavIcon info {str(e)}, continuing.")

        if self.status_code != 200 and not ignore_status_codes:
            screenshot = await capture_full_page(page=self.page, screenshot_format=self.screenshot_format, watch_uuid=watch_uuid, lock_viewport_elements=self.lock_viewport_elements)

            raise Non200ErrorCodeReceived(url=url, status_code=self.status_code, screenshot=screenshot)

        content = await self.page.content

        if not empty_pages_are_a_change and len(content.strip()) == 0:
            logger.error("Content Fetcher > Content was empty (empty_pages_are_a_change is False), closing browsers")
            raise EmptyReply(url=url, status_code=response.status)

        # Run Browser Steps here
        # @todo not yet supported, we switch to playwright in this case
        #            if self.browser_steps:
        #                self.iterate_browser_steps()


        # So we can find an element on the page where its selector was entered manually (maybe not xPath etc)
        # Setup the xPath/VisualSelector scraper
        if current_include_filters:
            js = json.dumps(current_include_filters)
            await self.page.evaluate(f"var include_filters={js}")

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Inspect attached screenshot and status_code
  2. Fix URL/auth/headers, or add proxy
  3. Enable 'Ignore status codes' in watch settings if appropriate
Defensive patterns

Strategy: try-catch

Try / catch

try:
    fetcher.fetch_page()
except Non200ErrorCodeReceived as e:
    record(e.status_code, screenshot=e.screenshot)
    if e.status_code in (429, 502, 503): backoff_and_retry()

Prevention

When it happens

Trigger: puppeteer fetch of a URL returning 403/404/5xx while 'ignore status codes' is disabled.

Common situations: Bot blocking (403), expired URL (404), upstream errors (502/503), missing auth.

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/6d2faa4b18ee99b1. Report an issue: GitHub.