dgtlmoon/changedetection.io · error · BrowserConnectError

Error while trying to connect the browser, Code {e.status_co

Error message

Error while trying to connect the browser, Code {e.status_code} (check your access, whitelist IP, password etc)

What it means

puppeteer/pyppeteer fetcher got websockets.exceptions.InvalidStatusCode while connecting to the browser WebSocket endpoint; wrapped as BrowserConnectError with the HTTP status hint about access/whitelist/IP/password.

Source

Thrown at changedetectionio/content_fetchers/puppeteer.py:299

        n = int(os.getenv("WEBDRIVER_DELAY_BEFORE_CONTENT_READY", 12)) + self.render_extract_delay
        extra_wait = min(n, 15)

        logger.debug(f"Extra wait set to {extra_wait}s, requested was {n}s.")

        from pyppeteer import Pyppeteer
        pyppeteer_instance = Pyppeteer()

        # Connect directly using the specified browser_ws_endpoint
        # @todo timeout
        try:
            logger.debug(f"[{watch_uuid}] Connecting to browser at {self.browser_connection_url}")
            self.browser = await pyppeteer_instance.connect(browserWSEndpoint=self.browser_connection_url,
                                                            ignoreHTTPSErrors=True
                                                            )
            logger.debug(f"[{watch_uuid}] Browser connected successfully")
        except websockets.exceptions.InvalidStatusCode as e:
            raise BrowserConnectError(msg=f"Error while trying to connect the browser, Code {e.status_code} (check your access, whitelist IP, password etc)")
        except websockets.exceptions.InvalidURI:
            raise BrowserConnectError(msg=f"Error connecting to the browser, check your browser connection address (should be ws:// or wss://")
        except Exception as e:
            raise BrowserConnectError(msg=f"Error connecting to the browser - Exception '{str(e)}'")

        # more reliable is to just request a new page
        try:
            logger.debug(f"[{watch_uuid}] Creating new page")
            self.page = await self.browser.newPage()
            logger.debug(f"[{watch_uuid}] Page created successfully")
        except Exception as e:
            logger.error(f"[{watch_uuid}] Failed to create new page: {e}")
            # Browser is connected but page creation failed - must cleanup browser
            try:
                await asyncio.wait_for(self.browser.close(), timeout=3.0)
            except Exception as cleanup_error:
                logger.error(f"[{watch_uuid}] Failed to cleanup browser after page creation failure: {cleanup_error}")
            finally:

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Verify the websocket URL includes required credentials/token (e.g. ws://host:3000/?token=...)
  2. Confirm host/port and that the browser service is up
  3. Whitelist the changedetection.io host IP with the browser provider
  4. Check for intercepting proxies that answer the WS upgrade with an error status

Example fix

# before
browser_connection_url = "ws://browser:3000"
# after
browser_connection_url = "ws://browser:3000/?token=YOUR_TOKEN"
Defensive patterns

Strategy: try-catch

Validate before calling

import socket, urllib.parse
u = urllib.parse.urlparse(browser_url)
assert u.scheme in ('ws','wss') and u.hostname and u.port
socket.create_connection((u.hostname, u.port), timeout=5).close()  # reachability pre-check

Try / catch

try:
    fetcher.fetch_page()
except BrowserConnectError as e:
    if 'Code 40' in str(e): fix_credentials_or_whitelist()
    else: raise

Prevention

When it happens

Trigger: pyppeteer connecting to browser_connection_url and the endpoint answers with an unexpected HTTP status — 401/403 from an auth-protected browser, 404 wrong path/port, or a proxy in between.

Common situations: Browserless/cloud browser service requiring a token not in the URL; wrong port; firewall/NAT rejecting the WS handshake; IP not whitelisted on a managed browser service.

Related errors


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