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

# Errors

> Structured error responses, machine-readable codes, retry guidance, and recovery patterns.

## Error Envelope

Every `/v1/` error response uses a structured envelope:

```json theme={null}
{
  "error": "Human-readable error message",
  "code": "AUTH_INVALID_KEY",
  "retryable": false,
  "requestId": "550e8400-e29b-41d4-a716-446655440000"
}
```

| Field        | Type    | Always present             | Description                                   |
| ------------ | ------- | -------------------------- | --------------------------------------------- |
| `error`      | string  | Yes                        | Human-readable error message                  |
| `code`       | string  | Yes                        | Machine-readable error code (see table below) |
| `retryable`  | boolean | Yes                        | Whether the request can be retried            |
| `retryAfter` | integer | Only on `429`              | Seconds to wait before retrying               |
| `details`    | array   | Only on `VALIDATION_ERROR` | Per-field validation errors                   |
| `requestId`  | string  | Yes                        | Trace ID from `X-Request-Id` header           |

**For agents:** branch on `code` for error handling, use `retryable` to decide whether to retry, and include `requestId` in support requests.

## Error Codes

| Code                     | HTTP | Retryable | When                                                      |
| ------------------------ | ---- | --------- | --------------------------------------------------------- |
| `AUTH_MISSING_KEY`       | 401  | No        | No API key in `x-api-key` or `Authorization` header       |
| `AUTH_INVALID_KEY`       | 401  | No        | Key hash not found or key is revoked                      |
| `AUTH_INVALID_CONTEXT`   | 401  | No        | Key record is missing workspace or user context           |
| `AUTH_FAILED`            | 500  | Yes       | Database error during key validation                      |
| `FORBIDDEN`              | 403  | No        | Workspace mismatch, app access denied, trigger inactive   |
| `NOT_FOUND`              | 404  | No        | App, execution, trigger, or template not found            |
| `VALIDATION_ERROR`       | 400  | No        | Invalid inputs, missing required fields, bad request body |
| `RATE_LIMITED`           | 429  | Yes       | Request rate exceeded; check `retryAfter`                 |
| `WEBHOOK_INVALID_URL`    | 400  | No        | Webhook URL malformed or unsupported protocol             |
| `WEBHOOK_INVALID_SECRET` | 401  | No        | Webhook secret does not match                             |
| `RESOURCE_UNAVAILABLE`   | 503  | Yes       | Service not configured or temporarily unavailable         |
| `INTERNAL_ERROR`         | 500  | Yes       | Unhandled server error                                    |

## HTTP Status Codes

| Status | Meaning                                      |
| ------ | -------------------------------------------- |
| `200`  | Successful read                              |
| `202`  | Execution accepted and queued asynchronously |
| `400`  | Invalid request body or inputs               |
| `401`  | Missing or invalid API key                   |
| `403`  | Access denied for this workspace or app      |
| `404`  | App or execution not found                   |
| `429`  | Rate limit exceeded                          |
| `500`  | Unexpected server-side failure               |
| `503`  | Service temporarily unavailable              |

## Interpreting `202 Accepted`

`POST /v1/apps/{appId}/runs` returns `202` when Lamina has accepted the job.

That does **not** mean outputs are ready yet. After a `202` response:

* store the returned run ID
* call `GET /v1/runs/{runId}/wait?timeout=60` (simplest)
* or poll `GET /v1/runs/{runId}` every 3-5 seconds
* or wait for your webhook callback

## Common Error Cases

### Authentication errors

```json theme={null}
{
  "error": "Missing API key",
  "code": "AUTH_MISSING_KEY",
  "retryable": false,
  "requestId": "abc-123"
}
```

Action:

* `AUTH_MISSING_KEY`: send `x-api-key` header or `Authorization: Bearer ...`
* `AUTH_INVALID_KEY`: verify the key value, check it has not been revoked, confirm correct environment
* `AUTH_INVALID_CONTEXT`: the key exists but is misconfigured — contact support

### Invalid inputs

Validation errors include a `details` array with one structured entry per problem:

```json theme={null}
{
  "error": "Invalid inputs",
  "code": "VALIDATION_ERROR",
  "retryable": false,
  "requestId": "abc-123",
  "details": [
    {
      "param": "Mention Key Ingredients",
      "code": "missing_no_default",
      "message": "\"Mention Key Ingredients\" is required: no default is configured for this parameter."
    },
    {
      "param": "Location",
      "code": "invalid_option",
      "message": "\"Location\": invalid option \"Mars\". Must be one of: Studio, Urban, Park"
    }
  ]
}
```

Each detail entry has:

| Field     | Type           | Meaning                                    |
| --------- | -------------- | ------------------------------------------ |
| `param`   | string \| null | The offending parameter name               |
| `code`    | string         | A machine-readable detail code (see below) |
| `message` | string         | A human-readable explanation               |

**Detail codes:**

| Code                 | When it fires                                                                |
| -------------------- | ---------------------------------------------------------------------------- |
| `missing_no_default` | A parameter has no default and you didn't supply a value for it              |
| `unknown_parameter`  | The input key doesn't match any parameter on this app                        |
| `invalid_option`     | You sent an option value that isn't in the allowed list                      |
| `invalid_media`      | A `url` parameter failed media validation (bad URL, unreachable, wrong type) |
| `invalid_type`       | You sent the wrong JSON type (e.g. a number where a string was expected)     |

**About `required` and defaults:** every parameter in the schema is returned with `required: true`, so agents always know to supply a value. If a parameter has a `default`, omitting it is still safe -- the workflow will run with the default. If it has no `default`, omitting it triggers a `missing_no_default` error with `400`.

**How to recover:**

1. Parse `details[]` and branch on `code`.
2. For `missing_no_default`: read the `param` field, look it up in your cached schema, and fill in a value.
3. For `unknown_parameter`: re-fetch `GET /v1/apps/{appId}` -- your cached schema may be stale.
4. For `invalid_option`: read the message for the allowed option list and pick one.
5. For `invalid_media` or `invalid_type`: fix the value and retry.

### Not found

```json theme={null}
{
  "error": "App not found",
  "code": "NOT_FOUND",
  "retryable": false,
  "requestId": "abc-123"
}
```

Action:

* verify the `appId` or `runId`
* confirm the resource is accessible to the workspace associated with your key

### Rate limit exceeded

```json theme={null}
{
  "error": "Too many requests, please try again later",
  "code": "RATE_LIMITED",
  "retryable": true,
  "retryAfter": 60,
  "requestId": "abc-123"
}
```

Action:

* wait `retryAfter` seconds before retrying
* read `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers
* avoid tight polling loops; prefer webhooks or the wait endpoint for long-running executions

## Current Rate Limit

All `/v1/*` endpoints are currently limited to **100 requests per minute per IP**.

## Retry Guidance

Use the `retryable` field to decide whether to retry. As a general rule:

**Safe to retry** (`retryable: true`):

* `RATE_LIMITED` -- wait `retryAfter` seconds first
* `INTERNAL_ERROR` -- transient server failure, use exponential backoff
* `AUTH_FAILED` -- transient database error
* `RESOURCE_UNAVAILABLE` -- service temporarily down
* Network timeouts while fetching status
* Idempotent reads (`GET /v1/apps`, `GET /v1/runs/{id}`)

**Do not retry unchanged** (`retryable: false`):

* `VALIDATION_ERROR` -- fix the inputs first
* `AUTH_MISSING_KEY` / `AUTH_INVALID_KEY` -- fix credentials
* `FORBIDDEN` -- access denied, won't change on retry
* `NOT_FOUND` -- wrong ID

## Support Checklist

If you need to debug an issue quickly, capture:

* `requestId` from the error response
* `code` from the error response
* request path and method
* app ID or execution ID
* timestamp
