# Errors

A failed call returns a non-2xx status and a JSON body with a `message`:

```json
{ "message": "Organisation ID is required." }
```

Errors that map to a known condition also carry a `code` and sometimes an `action`:

```json
{
  "code": "CB001",
  "message": "Payment method required",
  "action": "Add a payment method in Billing settings."
}
```

`message` is written for a person to read. `code` is the one to branch on - messages get reworded, codes do not.

## Status codes

| Status | Means | Do |
| --- | --- | --- |
| `400` | The body was wrong - a missing id, a bad value | Fix the request; retrying will not help |
| `401` | No token, or an expired one | Refresh the [session token](/reference/api/authentication#session-tokens) and retry once; an API key that returns this is revoked or expired |
| `402` | Billing is blocking the action, or the plan does not include API keys | See [Billing](/billing/billing) and [Authentication](/reference/api/authentication#plans) |
| `403` | Authenticated, but not allowed | Check the role and the [permission](#permissions) below |
| `404` | No such resource, or not yours | Do not distinguish these - the API deliberately does not |
| `409` | Conflicts with what already exists | Usually a duplicate name |
| `429` | Rate limited | Honour `Retry-After` when present |
| `5xx` | The platform, not you | Retry with backoff; if it persists, [contact us](https://www.light-cloud.com/contact) |

The default for a rejected request is `400`, not `422`.

## Error codes

The first letter is the service (`C` for console), the second the category, then a number.

### Deploy

| Code | Status | Message |
| --- | --- | --- |
| `CD001` | 500 | Container deployment failed |
| `CD002` | 400 | Container deployment failed due to invalid configuration |
| `CD003` | 500 | Container build failed |
| `CD004` | 500 | Failed to push container image |
| `CD005` | 500 | Failed to deploy container to cloud |
| `CD006` | 504 | Deployment timed out |
| `CD007` | 500 | Failed to delete container deployment |
| `CD010` | 500 | Static site deployment failed |
| `CD011` | 500 | Static site build failed |
| `CD012` | 500 | Failed to upload static files |

`CD003` and `CD011` are your build, not the platform - read the [deployment logs](/reference/api/deployments#retrieve-a-deployment).

### Auth

| Code | Status | Message |
| --- | --- | --- |
| `CA001` | 401 | Authentication failed |
| `CA002` | 403 | You do not have permission to perform this action |
| `CA003` | 403 | You do not have access to this organisation |

`CA001` with an API key means the key is wrong, revoked, or expired - the API does not distinguish between them.

`CA003` usually means the wrong `targetOrganisationId`, not a missing role. With a key it means the body named an organisation the key does not belong to; omit the field and it is filled in correctly.

### Billing

| Code | Status | Message |
| --- | --- | --- |
| `CB001` | 402 | Payment method required |
| `CB003` | 402 | Usage limit exceeded |

A `402` on **every** request made with an API key, rather than on one particular action, means the organisation's plan no longer includes keys - the entitlement is re-checked on each call, so a downgrade stops existing keys working. Move to a paid plan, or use the [MCP server](/deploy-with-ai/mcp-server), which works on every account.

### General

| Code | Status | Message |
| --- | --- | --- |
| `CG001` | 500 | Something went wrong |
| `CG002` | 400 | Invalid request |
| `CG003` | 404 | Resource not found |
| `CG004` | 409 | This action conflicts with existing data |
| `CG005` | 429 | Too many requests |
| `CG006` | 503 | Service temporarily unavailable |

### Platform services

| Code | Status | Message |
| --- | --- | --- |
| `CR001` | 503 | Deployment service is temporarily unavailable |
| `CR002` | 502 | Deployment service returned an error |
| `CR003` | 504 | Deployment service timed out |
| `CS001` | 503 | Database connection failed |
| `CS002` | 500 | Database operation failed |
| `CX001` | 502 | Failed to connect to GitHub |
| `CX002` | 502 | Payment service error |

All of these are worth retrying with backoff. `CR003` in particular does not mean the deploy failed - it means the answer was slow. Poll [environment status](/reference/api/environments#retrieve-status) before retrying the deploy, or you will start a second one.

## Permissions

`403` with `CA002` means your role in that organisation lacks the permission the endpoint checks:

| Permission | Endpoints |
| --- | --- |
| `read:projects` | Every list, get, status, logs, metrics, and activity call |
| `create:projects` | Creating applications, environments, and databases |
| `update:projects` | Update, scale, deploy, rollback, rename, move, domains, password rotation |
| `delete:projects` | Deleting applications, environments, and databases |
| `read:users` | Listing organisation members |
| `read:billing` | Invoices, usage, and payment methods |
| `update:all` | Organisation-level settings |

Read your role from [`/api/auth/profile`](/reference/api/authentication#retrieve-the-current-user). An API key holds one of two access levels rather than a role: read-only maps to `read:projects`, deploy-and-manage adds create, update, and delete. No key holds billing or organisation permissions, so those endpoints answer `403` to every key.

## Retrying safely

Creates are not idempotent - there is no idempotency key yet, so a retried `create` after a timeout can leave you with two applications. On a timeout, list first and check before retrying.

Deletes and updates are safe to repeat.

## Related

- [API overview](/reference/api): Conventions and rate limits.
- [Authentication](/reference/api/authentication): Tokens and refresh.
- [Contact](https://www.light-cloud.com/contact): When it is the platform, not you.
