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

# Troubleshoot system and 5xx error codes

> What to do when the API returns INTERNAL_ERROR, BAD_GATEWAY, UPSTREAM_ERROR, UNPROCESSABLE_ENTITY, INVARIANT_VIOLATION, MISSING_TENANT, or TENANT_NOT_FOUND — how to tell transient from deterministic, when to retry, and when to escalate.

# Troubleshoot system and 5xx error codes

The generic system codes — `INTERNAL_ERROR`, `BAD_GATEWAY`, `UPSTREAM_ERROR`,
`UNPROCESSABLE_ENTITY`, `INVARIANT_VIOLATION`, `MISSING_TENANT`, and
`TENANT_NOT_FOUND` — surface on requests that failed before the caller's
domain logic could run. They read as opaque platform failures, but most split
into two clear classes: **transient** (safe to retry) and **deterministic**
(retrying changes nothing — fix the request or escalate). This page tells you
which class a code belongs to and what to do about it.

## Step 1 — read the retry-safety flag on the envelope

Before you classify anything, look at the error envelope you got back. Each
response carries an `error` object with `code`, `message`, `status`, and
optional `details`, plus a `meta` block with `request_id` and `docs_url`:

```json theme={null}
{
  "error": {
    "code": "UPSTREAM_ERROR",
    "message": "The upstream provider is temporarily unavailable",
    "status": 502,
    "details": {
      "retryable": true,
      "retry_after_seconds": 2
    }
  },
  "meta": {
    "request_id": "req_9e4c71",
    "timestamp": "2026-09-09T14:02:11Z",
    "docs_url": "https://docs.orbit.devotel.io/errors/UPSTREAM_ERROR"
  }
}
```

Two opt-in fields decide whether you retry:

* **`details.retryable`**: when a transient class of failure is identified
  before the response is built, the envelope is stamped
  `"retryable": true`. That marks the response as designedly safe to retry —
  the platform has deliberately classified this failure as an availability
  blip, not a fault.
* **`details.retry_after_seconds`**: when the envelope also names a wait
  window, the response carries it both in `details` and as a standard
  `Retry-After` header. Honour that window before retrying instead of
  retrying immediately.

If neither field is present, fall back to the default assumptions in the
table below. In either case, keep the `meta.request_id` — it is the handle
support uses to trace the failure if you escalate.

<Note>
  One well-known transient class, `TENANT_SCHEMA_INCOMPLETE`, has its own
  runbook at
  [Troubleshoot TENANT\_SCHEMA\_INCOMPLETE (503)](/troubleshooting/tenant-schema-incomplete-503).
  Check for that code before treating a 503 as generic.
</Note>

## Step 2 — classify the code

The table below lists the system codes as they appear in responses. The
`status` field on a response is always authoritative for that response —
where a code can surface under more than one status, the table names the
value you most commonly see.

| Code                                  | Typical status | Class               | What it means                                                                                                                                                                                                                                                                                              | What to do                                                                                                                                                                                                                                                                                              |
| ------------------------------------- | -------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INTERNAL_ERROR`                      | 500            | Platform fault      | The platform failed to complete the request; the message has been sanitized so it contains no internal detail.                                                                                                                                                                                             | **Retry once, only if the operation is safe to repeat** — send a mutation again only when it is already idempotent (a read, or a write at an endpoint that accepts `Idempotency-Key`), and always with a fresh `Idempotency-Key`. Repeating a blind retry loop will not clear a genuine platform fault. |
| `UPSTREAM_ERROR`                      | 502            | Transient           | A provider the platform needed (carrier, email relay, AI provider, storage) timed out or returned an error mid-request, so the platform had to abort.                                                                                                                                                      | **Check the [status page](https://status.orbit.devotel.io) first** — if provider outages cluster, work is queued until the provider recovers. If not, retry with backoff.                                                                                                                               |
| `BAD_GATEWAY`                         | 502            | Transient           | The platform-side edge (the gateway layer that fronts the API) failed to reach the backend that serves your request.                                                                                                                                                                                       | Same as `UPSTREAM_ERROR` — check the status page, then retry with backoff.                                                                                                                                                                                                                              |
| `UNPROCESSABLE_ENTITY`                | 422            | Deterministic       | The request passed syntax but failed domain validation (a value the recipient, sender, or policy objects to). This is tenant-owned: it is telling you the **payload is wrong for the account data or policy**, not that the platform failed.                                                               | **Do not retry the same body.** Fix the field named in `details` and send the corrected request. Retrying the same payload returns the same 422 forever.                                                                                                                                                |
| `INVARIANT_VIOLATION`                 | 500            | Platform guard-trip | The platform's consistent-state guard rejected your request: something about the call would have violated a rule the platform enforces to keep its own state coherent (for example, an out-of-order state transition on a resource). This almost always means a bug the platform should not have produced. | **Report to support immediately** — do not retry; the same guard will trip again. Include `meta.request_id` and the request payload that triggered it.                                                                                                                                                  |
| `MISSING_TENANT` / `TENANT_NOT_FOUND` | 400 / 404      | Caller-fixable      | The tenant-context gate could not attach your request to an organization. The API key resolved, but the resolved organization is missing or no longer provisioned.                                                                                                                                         | Fix the credential first — check that you are addressing the right organization and key mode (live vs. sandbox). Do not retry until the key resolves cleanly. If the same key worked yesterday and fails today, escalate — something on the account has been removed.                                   |

<Note>
  `UNPROCESSABLE_ENTITY` and `VALIDATION_ERROR` are both 422 determinants —
  match on the response `code`, not the message text. Either way, the fix is in
  your payload, not in a retry.
</Note>

## Step 3 — the decision checklist

Walk these steps in order on any of the codes above:

1. **Read the envelope.** Capture `error.code`, `error.status`, and
   `meta.request_id`.
2. **Status page.** For `UPSTREAM_ERROR` or `BAD_GATEWAY`, check
   [status.orbit.devotel.io](https://status.orbit.devotel.io) before you
   retry or escalate — an active incident answers every question.
3. **Retry or fix.** Retry only when the envelope says `retryable: true`, or
   the operation is a read, or it accepts a fresh `Idempotency-Key` and you
   supply one. For a deterministic code (`UNPROCESSABLE_ENTITY`,
   `MISSING_TENANT`, `TENANT_NOT_FOUND`), fix the request and move on.
4. **Escalate.** If a retryable check, a payload fix, and one retry all
   return the same code, or the code is `INTERNAL_ERROR` or
   `INVARIANT_VIOLATION`, open a support ticket.

## Worked examples

A transient 503 stamped safe by the platform — the designed retryable
envelope you should pass straight through your retry loop:

```json theme={null}
{
  "error": {
    "code": "UPSTREAM_ERROR",
    "message": "Email provider temporarily unavailable",
    "status": 503,
    "details": {
      "retryable": true,
      "retry_after_seconds": 3
    }
  },
  "meta": {
    "request_id": "req_9e4c72",
    "timestamp": "2026-09-09T14:02:11Z",
    "docs_url": "https://docs.orbit.devotel.io/errors/UPSTREAM_ERROR"
  }
}
```

A deterministic 422 the platform will keep returning until the payload is
fixed:

```json theme={null}
{
  "error": {
    "code": "UNPROCESSABLE_ENTITY",
    "message": "Sender ID violates the destination format rules",
    "status": 422,
    "details": {
      "field": "from",
      "value": "ORBIT-SALES"
    }
  },
  "meta": {
    "request_id": "req_9e4c73",
    "timestamp": "2026-09-09T14:02:11Z",
    "docs_url": "https://docs.orbit.devotel.io/errors/UNPROCESSABLE_ENTITY"
  }
}
```

A 500 that should not be retried — escalate with `meta.request_id`:

```json theme={null}
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Unexpected platform failure",
    "status": 500
  },
  "meta": {
    "request_id": "req_9e4c74",
    "timestamp": "2026-09-09T14:02:11Z",
    "docs_url": "https://docs.orbit.devotel.io/errors/INTERNAL_ERROR"
  }
}
```

## Escalation

Open a support ticket when any of these holds:

* The status page shows no incident, yet `UPSTREAM_ERROR` or `BAD_GATEWAY`
  persists past a few minutes of backoff.
* The response says `retryable: true` and you have already retried within
  the stated window, but the same failure returns.
* The code is `INVARIANT_VIOLATION` or `INTERNAL_ERROR` — both name the
  platform.
* The code is `MISSING_TENANT` / `TENANT_NOT_FOUND` and a working key
  suddenly stops resolving its organization.

Include the `meta.request_id`, the payload body (minus any credentials), and
whether the failure is reproducible or intermittent.

## See also

* [Error code reference](/reference/error-codes) — every code the API can
  return.
* [Idempotency and billing gates](/troubleshooting/idempotency-and-billing-gates)
  — how `Idempotency-Key` makes a retry-safe mutation.
* [Rate limits](/troubleshooting/rate-limits) — the deliberately transient
  429 class.
* [Troubleshooting hub](/reference/troubleshooting-hub) — the entry point
  for every runbook.
