From 4641dce56f553d834764df0ae1aa86e030c6c8c9 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:27:35 +0000 Subject: [PATCH 1/2] Document proxy error codes and the X-Kernel-Proxy-Error header The proxy layer sets a typed code on every branded error response and the error page tells automation clients to read it, but neither the header nor the codes were published. Adds a Proxy Errors page listing all eleven codes with retry guidance, and a recovery pattern for sessions a person is driving, where nothing re-navigates on their behalf. Co-Authored-By: Claude Opus 5 --- docs.json | 3 ++- proxies/errors.mdx | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 proxies/errors.mdx diff --git a/docs.json b/docs.json index c519b3e..7eca560 100644 --- a/docs.json +++ b/docs.json @@ -214,7 +214,8 @@ "proxies/residential", "proxies/mobile", "proxies/isp", - "proxies/datacenter" + "proxies/datacenter", + "proxies/errors" ] }, "browsers/bot-detection/web-bot-auth", diff --git a/proxies/errors.mdx b/proxies/errors.mdx new file mode 100644 index 0000000..3a6e6de --- /dev/null +++ b/proxies/errors.mdx @@ -0,0 +1,58 @@ +--- +title: "Proxy Errors" +--- + +When a request cannot be completed at the proxy layer, Kernel serves a branded error page and sets the `X-Kernel-Proxy-Error` response header to a typed code. + +The header is set on every branded proxy-layer error response, including WebSocket and subresource requests where the page body never renders. The status is always `502`, including for timeouts, so use the header rather than the status to decide what went wrong. + +## Error codes + +| Code | What happened | Retry | +| --- | --- | --- | +| `upstream_timeout` | Kernel's connection to the upstream proxy provider did not complete within its deadline. | Yes | +| `provider_unreachable` | The upstream proxy provider could not reach the destination. | Yes | +| `upstream_connect_failed` | The connection to the destination failed. | Yes | +| `upstream_dns_failure` | The upstream proxy host could not be resolved. | Yes | +| `origin_tls_timeout` | The proxy reached the destination, but the destination did not complete its TLS handshake in time. | Yes | +| `restricted_route_unavailable` | The destination requires a specialized egress route and Kernel could not build one. Kernel will not fall back to a provider known to reject the destination. | Yes | +| `proxy_unavailable` | A failure inside Kernel's own proxy layer, not your automation or the destination. | Yes | +| `origin_response_incomplete` | The destination closed the connection before sending a complete response. | Only if safe. The destination may have received the request, so retry only when repeating the action cannot cause duplicate changes. | +| `provider_rejected` | The upstream proxy provider rejected the request before the destination connection was established. | No. Try a different proxy or proxy type, and check the credentials on a [custom proxy](/proxies/custom). | +| `provider_blacklisted` | The upstream proxy provider blocks this destination. | No. Try a different proxy or proxy type, or disable the proxy for this destination. | +| `destination_blocked` | Kernel policy blocks connections to internal and private addresses. | No. Use a public destination address. | + +## Recovering from a proxy error + +An agent driving the browser usually recovers on its own, because a failed step leads it to re-navigate. When a person is driving the browser instead — for example through an embedded [live view](/browsers/live-view) — nobody re-navigates, and a transient provider failure becomes a dead end. + +Read the header on a `502` and retry the navigation so those failures never reach the person holding the browser: + +```typescript Typescript/Javascript +const RETRYABLE = new Set([ + 'upstream_timeout', + 'provider_unreachable', + 'upstream_connect_failed', + 'upstream_dns_failure', + 'origin_tls_timeout', + 'restricted_route_unavailable', + 'proxy_unavailable', +]); + +async function navigate(page, url, attempts = 3) { + for (let attempt = 1; ; attempt++) { + const response = await page.goto(url); + const code = response?.headers()['x-kernel-proxy-error']; + + if (!code || !RETRYABLE.has(code) || attempt === attempts) return response; + + await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); + } +} +``` + +`origin_response_incomplete` is deliberately absent from that set. The destination may have already acted on the request, so retrying it automatically can duplicate a submission. + +## Observing proxy errors after the fact + +Proxy failures are also reported as `proxy_error` [browser telemetry](/browsers/telemetry/overview) events in the `network` category, carrying the same code. Use the header to recover in the moment, and telemetry to attribute failures per session and per URL afterwards. From 94d949ee23fbfd770d9994f5b5d4d47f2a3efcea Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Fri, 18 Sep 2026 22:04:15 +0000 Subject: [PATCH 2/2] Correct restricted_route_unavailable retry advice and telemetry coverage The .gov path replaces the generic retry guidance with creating and checking a compatible residential proxy, so the retry column now says so. Telemetry does not carry every header code: origin_response_incomplete is not collected, and unrecognized values arrive as unknown with the original in raw_code. Co-Authored-By: Claude Opus 5 --- proxies/errors.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proxies/errors.mdx b/proxies/errors.mdx index 3a6e6de..7eaeabf 100644 --- a/proxies/errors.mdx +++ b/proxies/errors.mdx @@ -15,7 +15,7 @@ The header is set on every branded proxy-layer error response, including WebSock | `upstream_connect_failed` | The connection to the destination failed. | Yes | | `upstream_dns_failure` | The upstream proxy host could not be resolved. | Yes | | `origin_tls_timeout` | The proxy reached the destination, but the destination did not complete its TLS handshake in time. | Yes | -| `restricted_route_unavailable` | The destination requires a specialized egress route and Kernel could not build one. Kernel will not fall back to a provider known to reject the destination. | Yes | +| `restricted_route_unavailable` | The destination requires a specialized egress route and Kernel could not build one. Kernel will not fall back to a provider known to reject the destination. | Yes, except on `.gov` destinations. There, create a new residential proxy with no targeting, country-only targeting, or a U.S. state, confirm it with [`POST /proxies/{id}/check`](https://kernel.sh/docs/api-reference/proxies/check-proxy-health), then use it for the session. | | `proxy_unavailable` | A failure inside Kernel's own proxy layer, not your automation or the destination. | Yes | | `origin_response_incomplete` | The destination closed the connection before sending a complete response. | Only if safe. The destination may have received the request, so retry only when repeating the action cannot cause duplicate changes. | | `provider_rejected` | The upstream proxy provider rejected the request before the destination connection was established. | No. Try a different proxy or proxy type, and check the credentials on a [custom proxy](/proxies/custom). | @@ -55,4 +55,6 @@ async function navigate(page, url, attempts = 3) { ## Observing proxy errors after the fact -Proxy failures are also reported as `proxy_error` [browser telemetry](/browsers/telemetry/overview) events in the `network` category, carrying the same code. Use the header to recover in the moment, and telemetry to attribute failures per session and per URL afterwards. +Proxy failures are also reported as `proxy_error` [browser telemetry](/browsers/telemetry/overview) events in the `network` category. Use the header to recover in the moment, and telemetry to attribute failures per session and per URL afterwards. + +The telemetry `code` does not cover every code above. `origin_response_incomplete` is not reported as a `proxy_error` event, and a header value the browser image does not recognize is reported as `unknown`, with the original value in `raw_code`. Read the header when you need the exact code.