ERR_TUNNEL_CONNECTION_FAILED is narrower than a generic browser failure and much less conclusive than it sounds. Chromium could not establish a usable tunnel through the configured proxy. It has not told you that the provider is down, the password is wrong, or the destination blocked the browser.
Treat the error as a boundary marker: the tunnel did not become usable. The reason is still open.
What Chromium actually reported
Chromium currently defines network error -111 in its network error list as a tunnel connection through the proxy that could not be established. For HTTPS through an HTTP proxy, the client first sends CONNECT host:port. RFC 9110 says a successful 2xx CONNECT response switches that connection into tunnel mode. A non-2xx response means the tunnel was not formed.
The current Chromium HTTP/1 proxy-tunnel implementation returns this generic error for a response version below HTTP/1.0, extra buffered bytes after a 200, and any CONNECT status other than 200 or 407. That is an implementation detail worth recording: the RFC permits any 2xx while this Chromium path currently accepts 200. Save both the Playwright version and browser.version() with the reproduction.
Work the checklist before changing the route
The worksheet below accepts only bounded observations. It does not ask for credentials, hostnames, target URLs, IP addresses, or session identifiers, and it does not make a network request. Start with what the clients recorded and change one input at a time.
Place the tunnel failure, one control at a time.
Six bounded observations narrow the next capture. This worksheet does not make a network request.
Nothing leaves this page. The checklist accepts no credentials, hostnames, URLs, IP addresses, headers, or session identifiers.
Record a control before naming a failing layer. Strongest supported layer: Evidence collection. Evidence: insufficient evidence.
Record a control before naming a failing layer.
No request scope, response event, configuration review, or control result is recorded.
This does not prove
- That the proxy, browser, runner, or destination caused the failure.
- That an HTTP response was absent. It may simply be missing from the capture.
Capture next
- Identify the failed request as the main navigation or a subresource.
- Record whether that request emitted a response event or only requestfailed.
- Run the direct controlled-page baseline before changing proxy settings.
Build one reproducible comparison
1. Capture the failed request
Record whether the failure belonged to the main navigation or a subresource. Save the page.goto() error token, request.failure()?.errorText, whether a navigation response event appeared, and the browser version. Start with the event sequence, not a screenshot.
2. Freeze one test
Use one controlled or explicitly authorized URL, one proxy endpoint, one runner, and Playwright's bundled Chromium. Start a fresh browser context, set retries to zero, and keep the deadline fixed. Do not swap the endpoint, browser channel, proxy scheme, and timeout in the same run.