# Error Reference

All Lathe Studio API errors follow a consistent envelope format, making them easy to handle programmatically.

---

## [Error Envelope](/content/docs/api/errors#error-envelope/index.html)

Every `4xx` and `5xx` response returns JSON with this shape:

```json
{
  "error": "invalid_request",
  "message": "The 'name' field is required.",
  "details": {
    "field": "name"
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable error code. Use this for programmatic handling. |
| `message` | string | Human-readable description. Safe to display to end users. |
| `details` | object \| null | Optional. Additional context such as the specific field that failed validation, limit values, etc. |

---

## [HTTP Status Codes](/content/docs/api/errors#http-status-codes/index.html)

| Status | Error Code | When It Occurs |
| --- | --- | --- |
| 400 | `invalid_request` | Request body or parameters are malformed or missing required fields |
| 401 | `unauthorized` | Missing, malformed, or revoked API key |
| 403 | `forbidden` | API key lacks the required scope for this operation |
| 404 | `not_found` | Resource does not exist, or the key cannot access it |
| 409 | `conflict` | Request conflicts with existing state (e.g. duplicate idempotency key with different body) |
| 422 | `unprocessable` | Request is well-formed but semantically invalid (e.g. `release_id` references a non-existent release) |
| 429 | `rate_limit_exceeded` | Monthly or per-minute rate limit exceeded |
| 500 | `internal_error` | Unexpected server error |
| 503 | `service_unavailable` | Planned maintenance or temporary outage |

---

## [Error Examples](/content/docs/api/errors#error-examples/index.html)

### 400 — Missing required field

```json
{
  "error": "invalid_request",
  "message": "The 'name' field is required.",
  "details": {
    "field": "name"
  }
}
```

### 401 — Invalid API key

```json
{
  "error": "unauthorized",
  "message": "Invalid or expired API key. Check your Authorization header."
}
```

Check that your header is formatted as `Authorization: Bearer pt_live_...` (with a space after `Bearer`).

### 403 — Missing scope

```json
{
  "error": "forbidden",
  "message": "This API key does not have the 'builds:create' scope.",
  "details": {
    "required_scope": "builds:create"
  }
}
```

Create a new API key with the required scopes. Scopes cannot be added to existing keys.

### 404 — Resource not found

```json
{
  "error": "not_found",
  "message": "Build not found."
}
```

The resource either doesn't exist or belongs to a different organization. Verify the ID.

### 422 — Semantic validation error

```json
{
  "error": "unprocessable",
  "message": "The specified release_id does not exist in this project.",
  "details": {
    "field": "release_id",
    "value": "rel_01HXYZ"
  }
}
```

### 429 — Rate limit exceeded

```json
{
  "error": "rate_limit_exceeded",
  "message": "You have exceeded your monthly API call limit.",
  "details": {
    "limit": 1000,
    "reset_at": "2026-04-01T00:00:00Z"
  }
}
```

See the `X-RateLimit-Reset` header for when the limit resets. See [Rate Limits](/content/docs/api/rate-limits/index.html) for retry guidance.

### 500 — Internal error

```json
{
  "error": "internal_error",
  "message": "An unexpected error occurred. Please try again or contact support."
}
```

Retry with exponential backoff. If the error persists, contact [support@lathe.studio](mailto:support@lathe.studio) with the timestamp and endpoint.

---

## [Idempotency](/content/docs/api/errors#idempotency/index.html)

For `POST` requests, include an `Idempotency-Key` header to safely retry without creating duplicate resources:

```http
Idempotency-Key: my-pipeline-run-12345
```

If a request with the same key succeeds, subsequent identical requests return the original response. If the body differs, a `409 Conflict` is returned.

Idempotency keys expire after 24 hours.
