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

# Error Handling

> DevHelm API error response format, HTTP status codes, and common error scenarios

All DevHelm API errors return a uniform JSON envelope. The shape is identical for every non-2xx response — only the populated fields differ by error class.

## Error response format

```json theme={null}
{
  "status": 400,
  "code": "VALIDATION_FAILED",
  "message": "Monitor name must not be blank",
  "timestamp": 1712956800000,
  "requestId": "5b6f7a8c-1234-4d5e-9f0a-1b2c3d4e5f6a",
  "errors": [
    {
      "code": "MONITOR_NAME_BLANK",
      "field": "name",
      "message": "must not be blank"
    }
  ]
}
```

| Field       | Type           | Description                                                                                                  |
| ----------- | -------------- | ------------------------------------------------------------------------------------------------------------ |
| `status`    | integer        | HTTP status code (mirrors the response status line)                                                          |
| `code`      | string         | Coarse machine-readable error category (e.g. `NOT_FOUND`, `RATE_LIMITED`); stable per status                 |
| `message`   | string         | Human-readable error description; safe to surface to end users                                               |
| `timestamp` | long           | Server time when the error was produced (epoch milliseconds)                                                 |
| `requestId` | string \| null | Opaque per-request identifier; same value as the `X-Request-Id` response header. Include in support tickets. |
| `errors`    | array \| null  | Structured per-field rejections; populated for validation errors (400), null otherwise                       |

Each entry in `errors` contains:

| Field     | Type           | Description                                                                               |
| --------- | -------------- | ----------------------------------------------------------------------------------------- |
| `code`    | string         | Stable machine-readable validation code (e.g. `MONITOR_HEARTBEAT_GRACE_EXCEEDS_INTERVAL`) |
| `field`   | string \| null | JSON-pointer-like path to the offending field, or null for request-wide errors            |
| `message` | string         | Human-readable description of the rejection                                               |

## HTTP status codes

| Status | Meaning               | Common causes                                                                          |
| ------ | --------------------- | -------------------------------------------------------------------------------------- |
| `400`  | Bad Request           | Validation errors, malformed JSON, invalid parameters                                  |
| `401`  | Unauthorized          | Missing, invalid, or revoked API key                                                   |
| `403`  | Forbidden             | Valid key but insufficient permissions for the organization                            |
| `404`  | Not Found             | Resource doesn't exist or isn't accessible in the current organization                 |
| `409`  | Conflict              | The request collides with current resource state (e.g. deploy lock, unique constraint) |
| `429`  | Too Many Requests     | Rate limit exceeded (see [Rate Limits](/patterns/rate-limits))                         |
| `500`  | Internal Server Error | Unexpected server error                                                                |
| `502`  | Bad Gateway           | An upstream provider returned an error                                                 |
| `503`  | Service Unavailable   | Temporary service disruption                                                           |

## Validation errors

When a request fails validation, the `errors` array contains one entry per rejected field. Each entry carries a stable `code` you can programmatically match on:

```json theme={null}
{
  "status": 400,
  "code": "VALIDATION_FAILED",
  "message": "Request validation failed",
  "timestamp": 1712956800000,
  "requestId": "a1b2c3d4-5678-90ab-cdef-111111111111",
  "errors": [
    {
      "code": "MONITOR_NAME_BLANK",
      "field": "name",
      "message": "must not be blank"
    },
    {
      "code": "MONITOR_FREQUENCY_OUT_OF_RANGE",
      "field": "frequencySeconds",
      "message": "must be between 30 and 86400"
    }
  ]
}
```

## Common error scenarios

### Authentication errors

```bash API theme={null}
# Missing API key
curl https://api.devhelm.io/api/v1/monitors
# → 401: {"status":401,"code":"UNAUTHORIZED","message":"Full authentication is required",...}

# Invalid key
curl https://api.devhelm.io/api/v1/monitors \
  -H "Authorization: Bearer dh_live_invalid"
# → 401: {"status":401,"code":"UNAUTHORIZED","message":"Invalid or revoked API key",...}
```

### Resource not found

```bash API theme={null}
curl https://api.devhelm.io/api/v1/monitors/nonexistent-id \
  -H "Authorization: Bearer $DEVHELM_API_TOKEN"
# → 404: {"status":404,"code":"NOT_FOUND","message":"Monitor not found",...}
```

### Entitlement limits

```bash API theme={null}
# Exceeding monitor limit on your plan
curl -X POST https://api.devhelm.io/api/v1/monitors \
  -H "Authorization: Bearer $DEVHELM_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
# → 400: {"status":400,"code":"ENTITLEMENT_EXCEEDED","message":"Monitor limit reached for your plan",...}
```

## Handling errors in code

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { DevhelmApiError, DevhelmRateLimitError } from "@devhelm/sdk";

  try {
    const monitor = await client.monitors.create({ ... });
  } catch (error) {
    if (error instanceof DevhelmRateLimitError) {
      const retryAfter = error.headers?.get("Retry-After");
      await sleep(parseInt(retryAfter ?? "5") * 1000);
    } else if (error instanceof DevhelmApiError) {
      console.error(`${error.code}: ${error.message}`);
    }
  }
  ```

  ```python Python theme={null}
  from devhelm import DevhelmApiError, DevhelmRateLimitError

  try:
      monitor = client.monitors.create(...)
  except DevhelmRateLimitError as e:
      print(f"Rate limited (code={e.code}), retry shortly")
  except DevhelmApiError as e:
      print(f"API error {e.status} [{e.code}]: {e.message}")
  ```
</CodeGroup>

## Rate limit errors

429 responses include extra headers — see [Rate Limits](/patterns/rate-limits) for details.
