# JSON output contract Every command returns structured JSON: ```json { "version": "2.1", "ok": true, "command": "click", "data": { "action": "click" } } ``` Errors include machine-readable codes and recovery hints: ```json { "version": "2.1", "ok": false, "command": "click", "error": { "code": "STALE_REF", "message": "Element at @e7 no longer matches the last snapshot", "suggestion": "Run 'snapshot' to refresh refs, then retry", "recovery": { "strategy": "refresh_snapshot_then_retry_original", "retryable": true, "requires_fresh_snapshot": true }, "disposition": { "delivery": "not_delivered", "retry": "safe" } } } ``` Version 2.1 replaces the 2.0 `error.retry_command` string with the structured `error.recovery` object and adds `error.disposition`. Consumers must select a recovery strategy from `recovery.strategy` only when `disposition.retry` is `safe`; command strings from older envelopes must not be executed blindly. The removed `retry_command` field has no compatibility alias. ## Error codes | Code | Meaning | |------|---------| | `PERM_DENIED` | Accessibility permission not granted | | `ELEMENT_NOT_FOUND` | No element matched the ref or query | | `APP_NOT_FOUND` | Application not running or no windows | | `STALE_REF` | Ref could not be re-identified in the live UI | | `AMBIGUOUS_TARGET` | Ref recovery matched multiple plausible targets | | `SNAPSHOT_NOT_FOUND` | Snapshot ID is missing or expired | | `POLICY_DENIED` | Physical/headed path blocked by policy | | `ACTION_FAILED` | The OS rejected the action | | `ACTION_NOT_SUPPORTED` | The target does not expose the requested action | | `APP_UNRESPONSIVE` | The matching application stopped responding | | `PLATFORM_NOT_SUPPORTED` | Adapter method not implemented on this platform | | `TIMEOUT` | Wait condition expired | | `INVALID_ARGS` | Invalid argument values | ## Exit codes `0` success, `1` structured error (JSON on stdout), `2` argument parse error.