Errors
Purpose
Document the public error-code contract, show where failures appear in CLI output versus result envelopes, and give concrete recovery steps for the most common host, validation, and runtime failures.
Sources
- Public error-code enum:
apps/node/src/contracts/errors.ts - Result envelope shape:
apps/node/src/contracts/result.ts - Execution validation:
apps/node/src/domain/executions/validateExecution.ts - CLI formatting:
apps/node/src/cli/output.ts
Two Failure Shapes
Clawperator surfaces failures in two main shapes.
1. Top-level CLI error object
This appears when Node fails before it can return an execution envelope, for example during argument parsing, payload validation, device selection, or host checks.
Example:
{
"code": "EXECUTION_VALIDATION_FAILED",
"message": "open_uri requires params.uri",
"details": {
"path": "actions.0.params.uri",
"actionId": "open-1",
"actionType": "open_uri"
}
}
Success condition for recovery:
- you change the command or payload
- rerunning no longer returns a top-level
{ "code": ... }object
2. Result envelope failure
This appears when Node successfully sends the command but the execution still fails at dispatch time or during one of the steps.
Per-step example:
{
"envelope": {
"commandId": "read-1",
"taskId": "read-1",
"status": "failed",
"error": "Step a1 (read_text) failed: NODE_NOT_FOUND",
"stepResults": [
{
"id": "a1",
"actionType": "read_text",
"success": false,
"data": {
"error": "NODE_NOT_FOUND",
"message": "No matching node found"
}
}
]
},
"deviceId": "emulator-5554",
"terminalSource": "clawperator_result",
"isCanonicalTerminal": true
}
Envelope-level example with errorCode:
{
"envelope": {
"commandId": "snapshot-1",
"taskId": "snapshot-1",
"status": "failed",
"stepResults": [],
"error": "Accessibility service is not available",
"errorCode": "SERVICE_UNAVAILABLE",
"hint": "Accessibility service not running. Run 'clawperator doctor --fix --device emulator-5554' to diagnose and repair, or 'clawperator operator setup --apk <path-to-apk> --device emulator-5554' to reinstall."
},
"deviceId": "emulator-5554",
"terminalSource": "clawperator_result",
"isCanonicalTerminal": true
}
Success condition for recovery:
envelope.status == "success"- every
stepResults[i].success == true
Public Error Codes
Only the codes defined in apps/node/src/contracts/errors.ts are part of the public error-code contract documented on this page.
Fast Triage
- If the output is a top-level object with
code, treat it as a Node-side failure before or outside the Android result envelope. - If
envelope.status == "failed"andstepResultsis empty, treat it as a dispatch, service, or envelope failure. - If
envelope.status == "failed"and one step hassuccess == false, branch on the first failed step'sdata.error. - Prefer exact codes over string-matching the human-readable
messageorerror.
What To Trust
Use these fields in order:
| Situation | Fields to inspect first |
|---|---|
| Top-level CLI error object | code, then details, then message |
| Envelope-level runtime failure | envelope.errorCode, then envelope.hint, then envelope.error |
| Per-step action failure | stepResults[i].data.error, then stepResults[i].data.message |
Notes:
errorCodeis optional on the envelope. Older APK behavior and some unclassified failures may leave it unset.- envelope
errorCodemay contain Android-emitted values such asSERVICE_UNAVAILABLEthat are not part of Node's publicerrors.tsenum - per-step failures do not use the envelope
errorCode; they usually expose the actionable code instepResults[i].data.error StepResult.datavalues are strings, so treatdata.erroranddata.messageas string fields- Node post-processing can turn some Android-internal failure markers into success results, for example normalizing
UNSUPPORTED_RUNTIME_CLOSEinto a successfulclose_appstep when adb pre-flight already succeeded
Recovery Patterns
| Family | Typical codes | What to do next |
|---|---|---|
| Device targeting | NO_DEVICES, DEVICE_NOT_FOUND, MULTIPLE_DEVICES_DEVICE_ID_REQUIRED |
Run clawperator devices, pick one device, and retry with --device <serial> |
| Interactive readiness | DEVICE_NOT_INTERACTIVE |
Wake or unlock the target, rerun clawperator doctor, and confirm the interactive-state check passes |
| Operator setup | OPERATOR_NOT_INSTALLED, OPERATOR_VARIANT_MISMATCH, OPERATOR_INSTALL_FAILED, OPERATOR_GRANT_FAILED, OPERATOR_VERIFY_FAILED |
Install or repair the expected Operator APK, then rerun the command |
| Host tooling | ADB_NOT_FOUND, ADB_SERVER_FAILED, HOST_DEPENDENCY_MISSING, ANDROID_SDK_TOOL_MISSING, SCRCPY_NOT_FOUND |
Repair the host environment before retrying |
| Daemon lifecycle and proxy | DAEMON_START_FAILED, DAEMON_STOP_FAILED, DAEMON_PROXY_ERROR |
Inspect the daemon log and metadata files under ~/.clawperator/. For DAEMON_PROXY_ERROR, inspect device state before retrying because the action may already have executed |
| Payload or flag validation | MISSING_ARGUMENT, EXECUTION_VALIDATION_FAILED, EXECUTION_ACTION_UNSUPPORTED, PAYLOAD_TOO_LARGE |
Change the command or payload. Do not retry unchanged |
| Dispatch or service availability | RESULT_ENVELOPE_TIMEOUT, RESULT_ENVELOPE_MALFORMED, BROADCAST_FAILED, DEVICE_ACCESSIBILITY_NOT_RUNNING, DEVICE_SHELL_UNAVAILABLE |
Run clawperator doctor, repair the reported issue, then retry |
| UI lookup or gesture | NODE_NOT_FOUND, NODE_NOT_CLICKABLE, CONTAINER_NOT_FOUND, CONTAINER_NOT_SCROLLABLE, GESTURE_FAILED, SECURITY_BLOCK_DETECTED |
Refresh state with snapshot, wait for UI readiness, or adjust selectors and scroll strategy |
| Recording state | RECORDING_ALREADY_IN_PROGRESS, RECORDING_NOT_IN_PROGRESS, RECORDING_SESSION_NOT_FOUND, RECORDING_PULL_FAILED, RECORDING_PARSE_FAILED, RECORDING_SCHEMA_VERSION_UNSUPPORTED |
Repair recording state or use the right session before retrying |
Key Cases
EXECUTION_VALIDATION_FAILED
Use this when the command or payload is structurally wrong before Android execution starts.
Common triggers:
- missing required action fields such as
open_uri.params.uri - invalid ranges such as
scroll_until.maxScrolls > 200 - selector parser violations such as mixing
--selectorwith shorthand flags - invalid JSON payloads for
clawperator exec
Typical output:
{
"code": "EXECUTION_VALIDATION_FAILED",
"message": "scroll_until params.maxScrolls must be an integer in [1, 200]",
"details": {
"path": "actions.0.params.maxScrolls",
"actionId": "scroll-1",
"actionType": "scroll_until"
}
}
Recovery:
- fix the payload
- rerun validation
- do not retry unchanged
MISSING_ARGUMENT
Use this for CLI commands that are missing a required positional argument or required flag value.
Common triggers:
execwithout a payloadwait-for-navwithout--timeoutread-valuewithout any label selector flags
Example:
{
"code": "MISSING_ARGUMENT",
"message": "wait-for-nav requires --timeout <ms>."
}
Recovery:
- add the missing argument or flag
- rerun the command
RESULT_ENVELOPE_TIMEOUT
Use this when Node dispatched the command but did not receive a valid [Clawperator-Result] envelope before the execution timeout expired.
Typical fields:
{
"code": "RESULT_ENVELOPE_TIMEOUT",
"message": "Timed out waiting for [Clawperator-Result]",
"details": {
"commandId": "snapshot-1",
"taskId": "snapshot-1",
"lastActionId": "a1",
"lastActionType": "snapshot",
"lastActionCaveat": "payload-last only; Android execution position is unknown",
"elapsedMs": 30000,
"timeoutMs": 30000
},
"hint": "No correlated Android log lines were captured. This often indicates an APK/CLI version mismatch or an accessibility service issue. Run 'clawperator doctor --device emulator-5554 --operator-package com.clawperator.operator.dev' to diagnose."
}
Recovery:
- run
clawperator doctor - if
hintmentions no correlated Android log lines, treat it as a compatibility or accessibility diagnostic path rather than a generic retry - confirm the accessibility service and operator package are healthy
- increase timeout only if the action legitimately needs more wall-clock time
OPERATOR_NOT_INSTALLED
Use this when the requested operator package is not installed on the selected device.
Typical recovery:
clawperator operator setup --apk <path-to-apk> --device <device_serial> --operator-package <package_name>
If you are doing local branch validation, prefer the debug package:
com.clawperator.operator.dev
OPERATOR_VARIANT_MISMATCH
Use this when the device has an installed Operator APK, but it is the other known package variant than the one requested.
Recovery options:
- pass the installed package via
--operator-package - or reinstall the intended APK variant
BROADCAST_FAILED
Use this when Node could not dispatch the execution payload broadcast to the selected Operator package.
Common triggers:
- wrong
--operator-packagefor the installed APK variant - device shell transport failure
- the Operator package is missing or not reachable through Android broadcast delivery
Recovery:
- run
clawperator doctor --device <serial> --operator-package <package> - verify package presence and variant compatibility
- rerun the failing command only after doctor reports the target ready
PAYLOAD_TOO_LARGE
Use this when a CLI, Serve, or execution payload exceeds a configured size limit before dispatch.
Recovery:
- split long action lists into smaller executions
- move large inline data into files or skill artifacts where the command supports that pattern
- for Serve requests, keep the JSON body under the documented
100kblimit
DEVICE_NOT_INTERACTIVE
Current shipped surface:
- the doctor check
readiness.device.interactive - direct execution preflight before dispatch
- high-level skill-wrapper pre-spawn checks in:
clawperator skills runPOST /skills/:skillId/run
Meaning:
- the target device is not currently ready for interactive automation
- the doctor check reports structured evidence telling you why:
screenOndeviceLockeduserUnlocked- execution and skill-wrapper failures use a generic top-level error message rather than exposing lock-state evidence on the public execution surface
- direct execution and high-level skill wrappers may make a bounded host-side wake attempt first when the screen is off
- if the device remains asleep, is still locked, or still requires post-boot unlock, the runtime returns this error instead of proceeding
Typical recovery:
- wake the device if
screenOn == false - unlock the device if
deviceLocked == true - complete the post-boot unlock if
userUnlocked == false - rerun
clawperator doctorand requirereadiness.device.interactive.status == "pass"
NODE_NOT_FOUND
This usually appears as a per-step failure in stepResults[i].data.error, not as the top-level CLI code.
Common triggers:
- the selector never matched
- the app navigated somewhere unexpected
- the UI had not settled before the action ran
- the target was inside a different scroll container
Recovery:
- run
snapshotorreadto inspect current UI state - add
waitorsleepbefore the failing action when appropriate - tighten or loosen the selector based on what the snapshot actually shows
- add a container selector or a scroll step if the target is off-screen
DEVICE_ACCESSIBILITY_NOT_RUNNING
This is a doctor- and readiness-related failure indicating the Operator accessibility service is not active.
Recovery:
- run
clawperator doctor --fix --device <serial> - if needed, reinstall the Operator package and re-enable accessibility access
Legacy CLI Usage Objects
Some CLI handlers and tests still emit usage-style objects with string codes that are not part of apps/node/src/contracts/errors.ts. Treat those as command-line usage failures, not as public stable error codes.
What this means for agents:
- if the code is in
errors.ts, you can branch on it as part of the public contract - if the code is not in
errors.ts, treat it as a CLI-specific usage object and prefer fixing the command shape rather than building long-term logic around that string - similarly, Android may emit envelope
errorCodevalues outside the Node enum; branch on them when present in the envelope, but do not confuse them with the documented Node-side top-levelcodecontract