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

# Error handling

> How errors are shaped, where they appear, and when to retry.

Every error has the same shape and a stable `code`. Branch on the code, not the message.

## Response shape

```json theme={null}
{
  "success": false,
  "error": {
    "code": "PARAMETER_OUT_OF_RANGE",
    "message": "One or more parameters are outside the supported range.",
    "details": { "parameter": "target", "value": 5, "min": 10, "max": 100, "unit": "percent" }
  },
  "meta": {
    "requestId": "req_8a2Bf3kP",
    "timestamp": "2026-06-01T10:30:00.000Z",
    "path": "/battery/device_abc123"
  }
}
```

| Field | |
| - | - |
| `error.code` | Stable identifier. Branch on this. |
| `error.message` | Fixed text for the code. Don't parse it. |
| `error.details` | Context for fixing the request, such as allowed ranges or what the device offers. Only present when relevant, so check before reading. |
| `meta.requestId` | Include this when contacting support. |

Successful responses use the same wrapper, with `data` in place of `error`.

## Where errors appear

Errors reach you in two places:

* **The HTTP response.** Validation, authentication, and capability errors are returned immediately, before anything is sent to the device.
* **The action.** Once a command is accepted (`202`), it can still fail at the device. The action's `state` becomes `failed` and `errorCode` holds the code. You'll see it on [`GET /actions/{id}`](/guides/push) and in the [`push.failed` webhook](/guides/webhooks/events).

```json theme={null}
{
  "id": "action_qjSucnQFAk",
  "state": "failed",
  "errorCode": "DEVICE_OFFLINE",
  "errorMessage": "The device is currently offline at the manufacturer."
}
```

A failed action carries `errorCode` and `errorMessage` only, never `details`.

## When to retry

| Status | Meaning | What to do |
| - | - | - |
| `400`, `422` | The request is invalid for this device. | Fix the request using `details`. Don't retry unchanged. |
| `401`, `403` | Key, access, or end-user account problem. | Check your key and environment, or ask the end user to reconnect. Don't retry. |
| `404` | Device or action not found. | Don't retry. |
| `409` | Conflicts with another action or the device's state. | See [Conflicts](/guides/push/conflicts). |
| `410` | Device offline, or the action expired. | Retry `DEVICE_OFFLINE` once the device is back. Resubmit an expired action. |
| `429` | Rate limited. | Retry with exponential backoff. |
| `502`, `503`, `504` | The manufacturer is unreachable or slow, or Amps has paused this capability. | Retry with exponential backoff. |
| `500` | Unexpected error. | Retry once, then contact support with the `requestId`. |
| `501` | Not available yet. | Don't retry. |

`DEVICE_NOT_CAPABLE` never changes on retry: the device doesn't offer what you asked for. `CAPABILITY_PAUSED` is temporary, so keep the feature and retry later. `INVALID_OEM_RESPONSE` is a `502` that won't clear on retry. Amps doesn't retry failed commands for you.

## Next steps

<CardGroup cols={2}>
  <Card title="Error codes" icon="list" href="/guides/error-handling/error-codes">
    Every code, its status, and what triggers it.
  </Card>

  <Card title="Conflicts" icon="code-merge" href="/guides/push/conflicts">
    Handle `409` responses when actions collide.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.