Error Codes
All API responses include structured error information. Use the code and retryable fields for programmatic error handling.
Response Format
Error responses follow this structure:
{
"success": false,
"error": "Element not found: button.submit",
"code": "ELEMENT_NOT_FOUND",
"hint": "Verify the selector exists. Try taking a snapshot first.",
"retryable": true // local/bridge mode only
}Cloud mode includes hint. Local bridge mode includes retryable. Both include code.
Cloud API Error Codes
Source: error-handler.ts — returned by all /api/sessions/* endpoints
| Code | HTTP | Retry | Meaning | Hint |
|---|---|---|---|---|
ELEMENT_NOT_FOUND | 404 | YES | CSS selector matched nothing | Take a snapshot to inspect the DOM |
ELEMENT_IN_IFRAME | 404 | YES | Element is inside an iframe | Use a frame-aware selector |
ELEMENT_IN_SHADOW_DOM | 404 | YES | Element is inside a shadow DOM | Use >>> shadow-piercing syntax |
NAVIGATION_TIMEOUT | 504 | YES | Page took too long to load | Increase timeout or check URL |
OPERATION_TIMEOUT | 504 | YES | Operation exceeded time limit | Simplify the operation or increase timeout |
SESSION_CLOSED | 410 | FATAL | Browser session was closed | Create a new session |
PAGE_NAVIGATED | 409 | YES | Navigation occurred during operation | Wait for navigation, then retry |
BROWSER_PROTOCOL_ERROR | 502 | YES | Browser internal communication error | Retry or create new session |
STORAGE_TIMEOUT | 503 | YES | Storage backend slow or unreachable | Retry in a few seconds |
STORAGE_ERROR | 502 | YES | Storage backend returned an error | Usually transient, retry |
BROWSER_POOL_UNAVAILABLE | 503 | YES | No browser instances available | Retry in 10-30 seconds |
SESSION_LIMIT_REACHED | 429 | YES | Maximum concurrent sessions reached | Delete unused sessions first (DELETE /api/sessions/:id), or wait up to 30 min for idle sessions to auto-expire, then retry |
PROVISIONING_FAILED | 503 | YES | Cloud machine provisioning failed | Retry in 30 seconds |
INVALID_API_KEY | 401 | NO | API key invalid or expired | Check or regenerate your API key |
EVALUATE_FAILED | 400 | FATAL | JavaScript evaluation error | Check script for syntax errors |
PAYLOAD_TOO_LARGE | 413 | NO | Request body exceeds size limit | Reduce payload size |
INSUFFICIENT_CREDITS | 402 | FATAL | Not enough credits for this operation | Purchase credits at /api/credits/checkout |
WS_DISCONNECTED | 503 | YES | Chrome extension WebSocket disconnected | Ensure the extension is running and connected |
CLI_TIMEOUT | 504 | YES | Server-side CLI agent timed out | Try a simpler task or increase timeout |
CLI_FAILED | 502 | YES | Server-side CLI agent process error | Check server logs; agent exited non-zero |
NETWORK_ERROR | 502 | YES | Target URL unreachable | Verify URL is correct and accessible |
INTERNAL_ERROR | 500 | YES | Unclassified server error | Check server logs |
Local Mode Error Codes
Source: local native host — run thinkrun attach <tabId> before issuing commands
| Code | HTTP | Retry | Meaning | Hint |
|---|---|---|---|---|
EXTENSION_NOT_CONNECTED | 503 | YES | Chrome extension is disconnected or still reconnecting | If the extension is not installed, open Chrome with ThinkRun. Otherwise wait briefly or run thinkrun doctor. |
EXTENSION_DISCONNECTED | 503 | YES | Extension disconnected mid-request | Run thinkrun doctor first. If the bridge is healthy, use thinkrun session debug --json for continuity details. |
REQUEST_TIMEOUT | 504 | YES | Native host request timed out (30s) | Retry or check extension responsiveness |
COMMAND_TIMEOUT | 504 | YES | Extension command execution timed out | Simplify the action or increase timeout |
TAB_NOT_FOUND | 410 | FATAL | Target tab was closed or the remembered local binding is stale | Run: thinkrun tabs, then thinkrun attach <tabId> for a live tab |
TAB_NOT_OWNED | 403 | FATAL | Agent session does not own this tab | Run: thinkrun attach <tabId> to claim this tab |
TAB_OWNED_BY_OTHER_SESSION | 403 | FATAL | Another local session owns the tab, or the local authority cache is stale | Run: thinkrun session debug --json first. If continuity is still yours, re-attach only to rebuild authority. If another live owner holds the tab, use thinkrun new-window or wait for release. |
SESSION_NOT_FOUND | 404 | FATAL | Agent session ID not registered in native host | Run: thinkrun attach <tabId> to register a session |
ELEMENT_NOT_FOUND | 404 | YES | CSS selector matched nothing | Check selector against page DOM |
NAVIGATION_FAILED | 502 | YES | Page load failed (net::ERR_*) | Verify URL is accessible |
SCRIPT_ERROR | 400 | FATAL | JavaScript syntax or runtime error | Fix the script |
TASK_TIMEOUT | — | NO | AI task exceeded configured timeout | Increase --timeout or break the task into smaller steps |
BRIDGE_UNREACHABLE | — | FATAL | Local bridge could not be reached before the attach or switch request was issued | Run: thinkrun doctor |
NATIVE_HOST_UNREACHABLE | — | FATAL | Client-side bridge route could not be reached after port revalidation | Run: thinkrun doctor. If a mutating command was in flight, verify page state before retrying. |
NATIVE_HOST_ERROR | — | FATAL | Native host bridge is not running or encountered a fatal error | Run: thinkrun doctor |
Blank status cells on native-host bridge errors mean the CLI surfaced a structured local error before any HTTP response was produced.
Native-host self-install (thinkbrowse-host --install) returns a non-zero exit code on partial manifest-write failures so scripts can detect incomplete installs.
Fatal vs Transient Errors
The task executor uses a smart circuit breaker to distinguish fatal errors (stop immediately) from transient errors (retry with backoff). The native-host bridge has its own circuit breaker that opens after 3 consecutive non-app errors (e.g. command timeouts). If thinkrun doctor reports a circuit-breaker trip, run thinkrun reset-connection to re-arm it without reloading the extension.
Fatal (stop immediately)
2 consecutive fatal errors aborts the task.
SESSION_CLOSED— browser goneTAB_NOT_FOUND— tab goneTAB_NOT_OWNED— run attachTAB_OWNED_BY_OTHER_SESSION— session debug before any re-attachSESSION_NOT_FOUND— run attachSCRIPT_ERROR— bad JavaScriptEVALUATE_FAILED— eval errorBRIDGE_UNREACHABLE— doctor before retrying attach, switch-tab, or focusNATIVE_HOST_UNREACHABLE— doctor + verify page state before manually re-issuing mutating commandsNATIVE_HOST_ERROR— run doctorINVALID_API_KEY— bad keyINSUFFICIENT_CREDITS— buy credits
Transient (retry with backoff)
5 transient errors within 60s aborts the task.
ELEMENT_NOT_FOUND— may appear during loadNAVIGATION_TIMEOUT— slow pageEXTENSION_NOT_CONNECTED— reconnectingBROWSER_PROTOCOL_ERROR— transient- HTTP 502, 503, 504, 524
Handling Errors Programmatically
# Check the code field in the response
RESP=$(curl -s -X POST "$BASE/api/sessions/$SID/click" \
-H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
-d '{"selector": ".nonexistent"}')
CODE=$(echo "$RESP" | jq -r '.code // empty')
case "$CODE" in
SESSION_CLOSED|TAB_NOT_FOUND)
echo "Fatal: session gone, create a new one" ;;
TAB_NOT_OWNED|SESSION_NOT_FOUND)
echo "Fatal: run thinkrun attach <tabId>" ;;
TAB_OWNED_BY_OTHER_SESSION)
echo "Inspect session debug first; same-lineage re-attach may still recover authority" ;;
BRIDGE_UNREACHABLE|NATIVE_HOST_ERROR)
echo "Fatal: run thinkrun doctor" ;;
NATIVE_HOST_UNREACHABLE)
echo "Fatal: safe reads already exhausted retries; run thinkrun doctor and verify page state before re-issuing mutating commands" ;;
INVALID_API_KEY|INSUFFICIENT_CREDITS)
echo "Fatal: fix credentials or buy credits" ;;
SESSION_LIMIT_REACHED)
echo "Delete unused sessions, then retry" ;;
ELEMENT_NOT_FOUND|NAVIGATION_TIMEOUT)
echo "Transient: retry after delay" ;;
EVALUATE_FAILED|SCRIPT_ERROR)
echo "Fatal: fix the script" ;;
esacTAB_OWNED_BY_OTHER_SESSION is the main nuanced case in local mode: check thinkrun session debug --json first, because a same-lineage re-attach can recover local authority without taking over a foreign tab.