Limits and errors
The limits on hosted sessions, the errors the API returns, and which of them are worth retrying.
Limits
- 75 browsers running or starting at once per account.
- Sessions last at most 4 hours.
- A session with no CDP command for 5 minutes is closed (change it with
idleTimeoutSec). - Every session starts with a fresh browser profile, deleted when the session ends, unless you use a named profile, which keeps cookies and site storage between sessions.
- For safety the browser cannot open local files (
file://), upload files from the server, reach private or internal networks, or send email on port 25. - Uploading a file works from your code — see Uploading files. Downloads stay on our server and are deleted with the session; to keep a file, fetch it from inside the page and return its contents.
- Chrome extensions cannot be loaded into hosted browsers.
- The browser does not forward console messages or page errors, so
page.on("console")andpage.on("pageerror")stay silent. Collect what you need inside the page and read it back withevaluate. Two driver calls depend on those events: see setContent and exposeFunction.
Errors
| Status | code | Meaning |
|---|---|---|
| 400 | — | The body is not JSON, or an option is invalid; the message says which. |
| 400 | UNKNOWN_VERSION | No release matches version; the message lists the ones you can pick. |
| 400 | TOO_LARGE | The whole run configuration as stored (task, schema, sealed secrets and the browser options) is capped at 60 KB; shorten them. |
| 400 | INVALID | A webhook or cookie import body is invalid; the message says which field (and which cookie). |
| 400 | TOO_MANY | A cookie import would leave the profile with more than 5000 cookies. Use mode "replace", or import fewer. |
| 400 | CHALLENGE_KEY_MISSING | challengeService has no key to use: pass challengeService.apiKey, store your key in the dashboard (Settings), or use key "managed". |
| 401 | — | Missing, malformed or revoked API key. |
| 401 | INVALID_LINK | A share, hand-off or replay link that is invalid, of the wrong kind, or expired. |
| 403 | NOT_CONTROL | Only a link that lets you take control can mark a hand-off done; this one is watch-only. |
| 402 | INSUFFICIENT_BALANCE | Balance below the minimum. Top up in the dashboard. |
| 402 | CHALLENGE_LIMIT | This month's challenge-service limit on our key is reached. Raise it in the dashboard (Settings), or use your own key. |
| 404 | NOT_FOUND | No session, run, profile, webhook or recording with that id on your account. |
| 409 | NOT_RUNNING | Live view, a reconnect or a hand-off asked for before the browser started or after it ended. |
| 409 | NOT_KEEPALIVE | This session cannot be reconnected. Start it with keepAlive: true. |
| 409 | PROFILE_IN_USE | Another session is already saving to that profile. Stop it, or open the profile with persist: false. |
| 409 | NOT_READY | The recording is still being uploaded. Try again shortly after the session ends. |
| 409 | HANDOFF_UNSUPPORTED | The server running this browser does not support hand-off yet. |
| 409 | NO_HANDOFF | Marked done, but no hand-off was asked for. |
| 409 | HANDOFF_EXPIRED | The hand-off timed out before it was marked done. |
| 409 | WEBHOOK_LIMIT | At most 5 webhooks per account. Delete one first. |
| 410 | ENDED | A live link whose session has ended. |
| 410 | GONE | A replay link whose recording is no longer available (failed, or past its retention). |
| 413 | TOO_LARGE | A cookie import would make the profile larger than 3.5 MB. |
| 429 | CONCURRENCY_LIMIT | Too many browsers running or starting at once. Close one first. |
| 429 | — | More than 60 create calls in a minute from one address. Slow down. |
| 429 | — | More than 60 cookie imports in 10 minutes on one account (or 30 new webhooks in 10 minutes, 10 webhook tests or 60 hand-off requests a minute). Wait and retry. |
| 503 | NO_CAPACITY | No free browser slot right now, or no server that can run what you asked for (runs, schema, secrets, recording, the challenge service). Retry after a few seconds. |
| 503 | NO_WORKER | The server running that session is not reachable at the moment. |
| 503 | NOT_CONFIGURED | Hosted browsers (or that part of them) are not configured on this server. |
| 503 | NOT_AVAILABLE | Notes, profiles, runs, recordings, events, hand-off, webhooks or the challenge service are not enabled on this server yet. |
Errors are JSON: { "error": "...", "code": "..." }. If the WebSocket connection itself is refused, create a new session: connect URLs are single-use and expire after two minutes. A refused WebSocket upgrade answers with an HTTP status and a JSON error: 409 when the URL was already used, the session was cancelled, was not started with keepAlive, or is already connected; 401 when the URL has expired.
What to retry
- Retry with backoff:
503 NO_CAPACITYand503 NO_WORKER(wait 1, 2, 4… seconds with some jitter, and give up after a handful), and a429without a code (the per-address rate limit). - Retry a few times with backoff: other
5xxanswers, and a connect that was refused before your script started (with a new session: the old connect URL is spent). - Never loop:
400(fix the request),401,402(add credit),429 CONCURRENCY_LIMIT(close a browser first) and409 PROFILE_IN_USE. A person has to act; retrying only burns requests.