> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfsky.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Diagnose failed starts, blocked pages, lost logins, timeouts, and CDP disconnections.

Reproduce the failure with one browser and keep it open for [inspection](/debugging). Record the request status, error body, and `x-cloud-tracing-uuid` response header before changing configuration.

## Find the failing step

| Failure                                | Start here                                                                             |
| -------------------------------------- | -------------------------------------------------------------------------------------- |
| Browser will not start                 | Read the API error `code`; check credentials, host, proxy, and account limits.         |
| Browser starts but the site is blocked | Inspect the page and target response, then compare proxy and session settings.         |
| Previously saved login is missing      | Check whether the profile was still running, then profile storage and browser context. |
| Navigation or scraping times out       | Check the wait condition, failed network requests, and the inactivity timeout.         |
| Connection closes during a job         | Check the inactivity timeout, active sessions, and cleanup from other workers.         |
| More workers reduce throughput         | Check request limits, browser capacity, target failures, and traffic.                  |

## The browser will not start

A `401 not_authorized` usually means the API token is absent or invalid. Send it as `X-Cloud-Api-Token` and use the base URL assigned to your account. A `namespace_not_allowed` error means the request reached a host that does not serve the account.

| Code                                                     | What to do                                                                                                                                                                                                                    |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cluster_is_full`, `service_unavailable`, `app_outdated` | Temporary. Retry with bounded backoff.                                                                                                                                                                                        |
| `parallel_browsers_limit_reached`                        | Stop a browser your job owns, or check `GET /users/browser-limits` before starting.                                                                                                                                           |
| `bad_proxy`, `tunnel_not_ready`                          | The browser started but could not reach the internet through the configured proxy. Check the proxy URL, credentials, and provider availability; `tunnel_not_ready` can be retried. See [proxy errors](/proxies#proxy-errors). |
| `browser_failed_to_start`, `profile_start_failed`        | Retry once. If it repeats, inspect `data` and the proxy configuration.                                                                                                                                                        |
| `region_not_chosen`                                      | Pick a region in the dashboard first.                                                                                                                                                                                         |

For proxy tier selection, see [choosing the tier](/proxies#choosing-the-tier). `"tier": "shared"` is for testing only and is not intended for production; use `"tier": "premium"` in production. With the shared tier, only `country` is accepted. Sending `type`, `region`, `city`, `pool`, `asn`, `lat` and `lon`, or a session field such as `session_minutes` returns `422 validation_error` with the message `the shared tier targets 'country' only`.

For other `422 validation_error` responses, inspect `data` for the field that failed. Creating a persistent profile requires a title and a fingerprint with an OS; starting a one-time session has different defaults.

See [errors](/errors) for the full code list. Avoid retrying a timed-out start without checking whether it created a browser.

<span id="website-blocking-you" />

## The site is blocking you

Confirm that you received a block or challenge page. HTTP `200` alone does not prove that the expected content loaded.

<span id="1-fix-your-proxies" />

<span id="location-matters" />

### Start with the proxy

Compare the same small test through another suitable proxy while keeping other settings unchanged. Check the exit country, connection type, and whether the IP changes during a session. A residential or mobile IP may behave differently from a datacenter IP, but no proxy type guarantees access.

If the job runs on `"tier": "shared"`, switch to `"tier": "premium"` before comparing anything else. The shared tier is for testing only and is not intended for production. Keep the IP stable when the site ties a login or challenge to it. More restrictive targeting can reduce availability; see [proxies](/proxies).

<span id="3-fix-your-browser-profile" />

<span id="check-the-fingerprint-agrees-with-the-proxy" />

### Check fingerprint overrides

Remove unnecessary overrides and retest. If you set language, timezone, user agent, screen, or device fields manually, make sure they describe the browser you intend to use. See [fingerprints](/fingerprints).

### Tune the fingerprint

If the block persists with default settings, try another OS. Some sites treat desktop and mobile traffic differently, and a fresh profile with a different device can pass where the current one does not:

```json theme={null}
{ "fingerprint": { "os": "android" } }
```

### Check the automation client

Make sure your Playwright or Puppeteer code does not override the browser: user agent, viewport, locale, timezone, or device emulation set by the framework replace parts of the profile's fingerprint. Connect with the defaults.

Use a patched build of Playwright or Puppeteer, such as Patchright or rebrowser.

Do not use the framework's default interactions for clicks, typing, and scrolling; use [Human commands](/human_emulation) instead.

### Keep the page intact

Detection scripts can inspect changes to `window`, `document`, `navigator`, and the DOM, including redefined properties and wrapped native functions. Do not use stealth plugins or other anti-detection libraries with Surfsky: their overrides can conflict with the browser's fingerprint configuration or introduce detectable changes. Check your own init scripts for the same problems.

<span id="2-slow-down" />

<span id="4-act-human" />

<span id="5-still-getting-captchas" />

<span id="slow-down" />

<span id="still-getting-captchas" />

### Check the request pattern

Reduce concurrency and repeat a single user flow. Wait for each page state before triggering the next action. Use Human commands for input and follow [Human behavior](/human-behavior) for action order and timing. Compare the results after each change.

Test navigation through the site's search, menus, or internal links if opening the target URL directly fails. Where the task allows, vary the route between sessions.

If the target requires many navigation steps and the site allows it, you can change an existing internal link's destination and click it. The navigation then originates from that page. This modifies the DOM and may be detected (see [Keep the page intact](#keep-the-page-intact)), so test it on the target first.

If a challenge appears, confirm the selected [CAPTCHA solver](/captcha-solving) is enabled and observe its completion or failure events.

<span id="persistent-profiles-save-time" />

### Keep a profile that got through

On a well-protected site a browser is often let in only after a proof-of-work check, a CAPTCHA, or a few pages of ordinary browsing. A one-time profile has to earn that again on every start, and each attempt is a fresh chance to be blocked.

Once a session gets through, keep it. Run such jobs on a [persistent profile](/sessions#persistent-profiles) with cookies enabled, plus local storage if the site keeps its clearance there. Stop the session through the API so the state is saved, then start the same profile for the next run. The site sees a returning browser with valid cookies instead of a new one to challenge.

Keep the proxy stable for that profile. A clearance tied to one IP is useless from another; see [session control](/proxies#session-control).

## Sessions that will not stay logged in

Work through the [checklist in cookies](/cookies#sessions-that-will-not-stay-logged-in) for storage options, browser context, and expired logins. Two causes come from session handling:

* **The profile was still running.** Starting a running profile returns the existing session and ignores the settings in the new request, so a proxy, fingerprint, or storage change appears to have no effect. Check `GET /profiles/active`, stop the session, then start again.
* **The previous session ended in a crash or infrastructure failure.** An explicit stop and an inactivity stop both save the enabled storage. A crash does not, so anything done after the last saved stop is lost.

Importing cookies alone may not reproduce a login that depends on other browser state.

## Navigation and scraping timeouts

Open the Network panel and identify which request or page condition never completed. `networkidle` may never occur on a site with continuous background traffic. Try `domcontentloaded` followed by a selector that proves the required content is ready.

Check request blocking: a blocked script, API endpoint, or challenge resource can leave the page incomplete. Increasing the timeout will not restore a resource you prevented from loading.

A wait that outlives the [inactivity timeout](#the-browser-stopped-by-itself) ends with a closed target, not a timeout error from your framework.

The [Scraping API](/quickstart/scraping_api) returns `400 scrape_timeout` when the whole request exceeds 120 seconds. Its per-page `timeout` option, 30000 ms by default, applies to each navigation or selector wait inside that budget. Keep batches and wait conditions within it.

## The browser stopped by itself

A browser stops after `browser_settings.inactive_kill_timeout` seconds without activity. The default is 30 seconds; the maximum is 3600. Activity means CDP commands sent by your client, ChromeDriver requests, and Scraping API calls. A connection that sends nothing, an idle DevTools tab, and a screencast viewer do not count.

One long client-side wait can be a single CDP command. A `waitForSelector` with a 60-second timeout under the default setting can fail with `Target closed` because the browser stopped at 30 seconds. Raise the timeout for such waits and for manual inspection:

```json theme={null}
{ "browser_settings": { "inactive_kill_timeout": 300 } }
```

A longer timeout also leaves an abandoned browser running, and counting against your [concurrency limit](/concurrency), for longer. See [how a session ends](/sessions#session-lifetime).

<span id="cdp-errors-in-logs" />

<span id="cdp-errors-you-can-ignore" />

## CDP errors and disconnections

```text theme={null}
Protocol error Target.detachFromTarget: Target closed
Protocol error (Target.sendMessageToTarget): No session with given id
```

These messages can occur during normal tab closure or cleanup. Treat them as harmless only if the expected work already completed. During an active job, check whether the page closed, the browser crashed, the inactivity timeout expired, or another worker stopped the session.

Use `GET /profiles/active` to confirm the browser still exists. A stopped session's connection URL will not restart it: the WebSocket opens and closes at once with the reason `Profile not found`. Start a session and use the returned URL.

<span id="session-page-limit" />

## Page limit

```text theme={null}
Protocol error (Target.createTarget): User page limit reached
```

The browser already has five automation-created pages. The limit is per browser and shared by every CDP client connected to it. It counts `Target.createTarget` calls, including ones still in flight. The initial tab and popups opened by the site are not counted. A slot is released when your client closes the page or the browser destroys it.

Close pages you no longer need, or reuse one. For independent parallel work, use separate browsers within your account's concurrency limit.

<span id="framework-notes" />

<span id="framework-specific-tips" />

<span id="performance-problems" />

<span id="playwright" />

<span id="puppeteer" />

<span id="quick-wins" />

<span id="selenium" />

## Everything feels slow

Measure startup, navigation, interaction, and extraction separately. Check [speed optimization](/speed-optimization) for reducing commands, waits, and downloads. Increase concurrency only while completed results improve.

For framework-specific connection and cleanup details, use the [Playwright](/quickstart/playwright), [Puppeteer](/quickstart/puppeteer), or [Selenium](/quickstart/selenium) quickstart.

<span id="still-need-help" />

## Reporting a problem

Send [support](mailto:hello@surfsky.io):

* A minimal request or script that reproduces the failure.
* The HTTP status, error response, and `x-cloud-tracing-uuid` header.
* The approximate time in UTC and the API host used.
* The expected page result and what happened instead.

Share the `internal_uuid` privately if support needs the session identifier. Remove API tokens, proxy passwords, cookies, provider keys, and live connection URLs before sharing logs or screenshots.
