---
audience: developer
summary: The `SW_*` codes used in every error response.
title: Error codes
path: reference/error-codes
status: published
---

# Error codes

Every error response has the shape:

```json
{ "error": { "code": "SW_*", "message": "..." } }
```

`code` is stable across releases. `message` is human-readable and
may be localised. Match on `code` in code.

## Auth (`SW_AUTH_*`)

| Code | HTTP | Meaning |
|---|---|---|
| `SW_AUTH_INVALID_TOKEN` | 401 | Token failed validation. |
| `SW_AUTH_EXPIRED_TOKEN` | 401 | Token expired. Refresh. |
| `SW_AUTH_OIDC_FAILED` | 400 | OIDC code exchange failed. |
| `SW_AUTH_REDIRECT_MISMATCH` | 400 | `redirect_uri` doesn't match config. |
| `SW_AUTH_INSUFFICIENT_PERMISSION` | 403 | You lack the required permission/role. |
| `SW_AUTH_TENANT_MISMATCH` | 403 | Token's `tenant_id` doesn't match the resource. |

## Tenant / membership

| Code | HTTP | Meaning |
|---|---|---|
| `SW_TENANT_NOT_FOUND` | 404 | Tenant id doesn't exist. |
| `SW_TENANT_CAPACITY` | 409 | One of the tenant's limits hit. |
| `SW_NOT_MEMBER` | 403 | Caller is not a member of the resource. |

## Rooms

| Code | HTTP | Meaning |
|---|---|---|
| `SW_ROOM_NOT_FOUND` | 404 | |
| `SW_ROOM_FORBIDDEN` | 403 | |
| `SW_ROOM_FROZEN` | 409 | Closed or archived. |
| `SW_ROOM_TYPE_INVARIANT` | 400 | Op invalid for this `room_type`. |
| `SW_ROOM_CAPACITY` | 409 | Tenant max_participants reached. |
| `SW_ROOM_EXPORT_REFUSED` | 400 | Sidekick/bridge/incognito/federated. |
| `SW_ROOM_IMPORT_REFUSED` | 400 | Same. |

## Events / messages

| Code | HTTP | Meaning |
|---|---|---|
| `SW_EVENT_NOT_FOUND` | 404 | |
| `SW_EVENT_TOO_OLD_TO_EDIT` | 409 | Past the edit window. |
| `SW_REDACT_FORBIDDEN` | 403 | Not yours and you lack power. |
| `SW_CONTENT_TOO_LARGE` | 413 | > 64 KB. |
| `SW_RATE_LIMIT_EXCEEDED` | 429 | Tenant rate cap hit. |

## Notes

| Code | HTTP | Meaning |
|---|---|---|
| `SW_NOTE_NOT_FOUND` | 404 | |
| `SW_NOTE_FORBIDDEN` | 403 | |
| `SW_NOTE_CONFLICT` | 409 | Concurrent edit; resolve via CRDT path. |
| `SW_NOTE_QUOTA_EXCEEDED` | 409 | |

## Workspaces

| Code | HTTP | Meaning |
|---|---|---|
| `SW_WORKSPACE_NOT_FOUND` | 404 | |
| `SW_WORKSPACE_FORBIDDEN` | 403 | |
| `SW_WORKSPACE_NEEDS_OWNER` | 403 | |
| `SW_WORKSPACE_SLUG_TAKEN` | 409 | |

## AI

| Code | HTTP | Meaning |
|---|---|---|
| `SW_AI_NO_TOKEN` | 503 | No ScaiGrid token available. |
| `SW_AI_MODEL_NOT_FOUND` | 404 | Model not registered with this tenant. |
| `SW_AI_BUSY` | 409 | Already generating. |
| `SW_AI_BUDGET_EXCEEDED` | 429 | Monthly token cap hit. |
| `SW_PLUGIN_FORBIDDEN` | 403 | Plugin disabled for tenant/room. |
| `SW_PLUGIN_NOT_FOUND` | 404 | |
| `SW_PLUGIN_TIMEOUT` | 504 | Tool call exceeded 30s. |

## Plans

| Code | HTTP | Meaning |
|---|---|---|
| `SW_PLAN_NOT_FOUND` | 404 | |
| `SW_PLAN_NOT_RUNNING` | 400 | Op requires running. |
| `SW_PLAN_NOT_PAUSABLE` | 400 | |
| `SW_PLAN_NOT_RUNNABLE` | 400 | |
| `SW_PLAN_TOO_MANY_STEPS` | 400 | > 20 steps. |
| `SW_PLAN_EMPTY_STEPS` | 400 | |
| `SW_PLAN_EMPTY_GOAL` | 400 | |
| `SW_PLAN_UNKNOWN_STEP` | 400 | |
| `SW_PLAN_STEP_TERMINAL` | 400 | Step already done/failed/skipped. |
| `SW_PLAN_BAD_MODE` | 400 | |
| `SW_PLAN_BAD_ADVANCE` | 400 | |
| `SW_PLAN_BAD_NEXT_ACTION` | 400 | |
| `SW_PLAN_NEEDS_SIDEKICK_ROOM` | 400 | `venue=sidekick` without room id. |
| `SW_PLAN_BAD_VENUE` | 400 | |

## Sidekicks

| Code | HTTP | Meaning |
|---|---|---|
| `SW_SIDEKICK_NOT_FOUND` | 404 | |
| `SW_SIDEKICK_CAP_REACHED` | 429 | Per-user / per-tenant concurrency hit. |
| `SW_SIDEKICK_TIMEOUT` | 504 | Task exceeded its wall-clock cap. |

## Calls

| Code | HTTP | Meaning |
|---|---|---|
| `SW_CALL_NOT_FOUND` | 404 | |
| `SW_CALL_ENDED` | 410 | Call already terminated. |
| `SW_CALL_LIVEKIT_UNREACHABLE` | 503 | Backend ops issue. |
| `SW_CALL_RECORDING_DISABLED` | 403 | Tenant has recordings off. |

## Federation

| Code | HTTP | Meaning |
|---|---|---|
| `SW_FED_PEER_UNKNOWN` | 403 | Sender server not in `allowed_peers`. |
| `SW_FED_SIGNATURE_MISMATCH` | 401 | Event signature failed verification. |
| `SW_FED_PROTOCOL_VERSION` | 400 | Unsupported protocol version. |
| `SW_FED_REFUSED` | 403 | Room or tenant has federation disabled. |

## Bridges

| Code | HTTP | Meaning |
|---|---|---|
| `SW_BRIDGE_NOT_FOUND` | 404 | |
| `SW_BRIDGE_SIGNATURE_MISMATCH` | 401 | Inbound HMAC failed. |
| `SW_BRIDGE_PAUSED` | 409 | Bridge is paused. |
| `SW_BRIDGE_TRANSPORT_ERROR` | 502 | Foreign system unreachable. |

## Prompt Studio

| Code | HTTP | Meaning |
|---|---|---|
| `SW_STUDIO_DISABLED` | 403 | Tenant flag off. |
| `SW_STUDIO_FORBIDDEN` | 403 | Caller not in allowlist. |
| `SW_STUDIO_NO_PARTICIPANT` | 404 | No AI participant resolved. |

## Misc

| Code | HTTP | Meaning |
|---|---|---|
| `SW_NOT_FOUND` | 404 | Generic. |
| `SW_VALIDATION_ERROR` | 422 | Pydantic schema validation failed (details in `error.details`). |
| `SW_INTERNAL_ERROR` | 500 | Bug. Includes a `request_id` to grep server logs. |
| `SW_SERVICE_UNAVAILABLE` | 503 | Downstream (DB / Redis / NATS) unreachable. |
