> ## 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.

# Troubleshooting: CHANNEL_UNAVAILABLE and fallback chain exhaustion

> Resolve 422/503 CHANNEL_UNAVAILABLE when no fallback route is configured or every hop in the fallback ladder failed — name the failing hop, distinguish transient skips from real outages, and edit the chain in Settings → Channels.

# Troubleshooting: CHANNEL\_UNAVAILABLE and fallback chain exhaustion

`CHANNEL_UNAVAILABLE` surfaces in two casings. A **422** means the routing
configuration itself stops the send — no sender or provider can fix it from
the outside. A **503** means the platform attempted your channels and every
route it tried came back unavailable — either nothing is configured to fall
back to, or the ladder was walked to its end. Both casings are tenant-owned:
the fix lives in your channel configuration, your fallback map, or your
send pacing — not in a provider ticket.

This page maps the error to the hop that failed, the reason the ladder ran
out, and the control you own to clear it.

## What the error means

The router resolves a send in two passes:

1. **Configuration pass (422).** The channel is not connected on your
   account, no hop in your fallback chain is eligible, or a campaign
   pre-flight gate (a RED WhatsApp quality rating) aborts before any
   provider is contacted. Retrying unchanged replays the same refusal.
2. **Attempt pass (503).** The router walked every provider on the primary
   channel, then every channel your fallback map lists, and each came back
   unavailable. The failure is transient (a circuit breaker skipped the
   provider for a window) or real (a genuine outage) — the response tells
   you which through `details.transient_skip`.

An empty fallback list for a primary channel means the attempt pass has
nowhere to go: the first channel failure ends the chain and returns 503 to
the caller.

## The three operation classes that surface it

The same code surfaces on three send paths. Triage the one you hit:

| Path                                                | When it fires                                                                                                                                                        | Typical `details`                                    |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| **Message send** (`POST /messages`, `POST /notify`) | The send walked every reachable channel and none delivered. A waterfall carry-over (`fallback.chain_exhausted` in your logs) means the cascade had no remaining hop. | `last_error`, `transient_skip`, per-hop attribution  |
| **Campaign execution**                              | A pre-flight gate refuses the launch (422), or the per-recipient walk exhausts (503) and the campaign records the failure per contact.                               | `failure_code`, `auto_paused_reason` on the campaign |
| **Verify factor spawn** (`POST /verify/start`)      | Every factor channel your session can use (SMS, voice, email, per your ordered chain) failed; no factor was spawned.                                                 | `transient_skip`, `last_error`                       |

## Per-channel ladder

Each primary channel has its own ladder shape. The verify chain is the only
one with a platform-fixed order.

### SMS and WhatsApp (org fallback map)

Your org-level cross-channel fallback map (Settings → Channels) lists, per
primary channel, the ordered channels to try when the primary fails.
Example: a chain `rcs → ["sms", "email"]` sends on RCS, retries the body
on SMS if RCS fails, and ends on email if both fail. The adaptive cascade
may **re-rank** a chain like `[whatsapp, rcs, sms]` by segment-learned
delivery outcomes before the first attempt — so the order you configured
is the default, not necessarily the order executed.

A cascade-armed send (`POST /notify`) exhausts like this: RCS fails
(carrier-level), the walk falls to SMS (carrier-level failure again), then
to email, and once email also fails the walk terminates with
`fallback.chain_exhausted` on the fallback metric and 503 to the caller.

### Verify factor chain

The verify chain is **platform-ordered SMS → voice → email** and is not
editable from your fallback map. A verify send that fails on SMS
automatically falls to voice, then email; when all three are unavailable
the `POST /verify/start` call returns 503 `CHANNEL_UNAVAILABLE`. The same
`details.transient_skip` flag tells you whether the router skipped the
providers (a breaker window — retry safe) or genuinely failed each one.

## Edit the fallback map in Settings → Channels

Fix the chain, not the symptoms:

1. Open **Settings → Channels** in the dashboard.
2. Find the **Cross-channel fallback** section and edit the channel that
   fired — add a hop (the ordered list gains a tail), remove a stale hop,
   or reorder the list so a reachable channel is tried earlier.
3. Save. The per-channel cache invalidates immediately — your next send
   walks the new chain.

From the API, the same map lives on **`GET /api/v1/settings/channels-fallback`** and
**`PUT /api/v1/settings/channels-fallback`**: send a JSON object keyed by
primary channel whose values are ordered lists (e.g. `{ "rcs": ["sms"] }`);
pass an empty array to disable fallback for that primary explicitly.
Self-fallback entries and duplicates are dropped server-side.

A second, per-campaign shape exists: an omnichannel campaign's `channels[]`
array is an ordered **ladder** that layers on the org map with
per-hop `fallback_on` triggers and `fallback_after_seconds` windows. The
org map fires when no ladder is configured, or when the ladder's own
triggered hops all fail.

## The `details.transient_skip` flag — when retry is safe

Every 503 carries a `details.transient_skip` boolean. Branch on it before
you page anyone:

* **`transient_skip: true`** — every provider on the channel was skipped
  because its per-tenant circuit breaker was open or the channel was
  rate-limited in the window before your send. No provider was contacted —
  nothing to fix, and the breaker clears on its own. **Retrying the send
  is safe** and is what the flag exists to signal: hold a short backoff
  (the cooldown window) and resubmit.
* **`transient_skip: false`** — at least one provider was attempted and
  genuinely failed. A retry transmits the same failure unless the upstream
  fault cleared in the meantime. **Investigate before retrying**: if every
  chain hop on every send for a channel fails with `transient_skip: false`
  for more than a few minutes, the outage is real — open a ticket only
  after you've ruled out your own fallback map and sender configuration.

Audit rule: a circuit-breaker window produces one incident, not one per
send. Repeated `transient_skip: true` rejections on the same channel
within the same minute are one breaker event; a second distinct channel
failing in the same window moves the diagnosis from your map to the
provider layer.

## What to capture before escalating

Work the ladder mapping first. If a send still reads as genuinely
unavailable many minutes after the breaker window should have cleared —
or a 422 keeps firing after you added a valid hop — open a ticket with:

1. **The error envelope** — code, HTTP status, and the full `details`
   object (`transient_skip`, `last_error`, any per-hop attribution).
2. **The send path** — single message via `POST /messages`, a notify
   cascade, a campaign, or a verify spawn.
3. **The primary channel and the chain as currently configured** — the
   `GET /settings/channels-fallback` body verbatim.
4. **Your organization ID** — Settings → Organization, or
   `organizationId` from `GET /api/v1/me`.

## What not to do

* **Do not retry a 422.** The refusal re-fires identically until the
  configuration changes.
* **Do not retry a `transient_skip: true` 503 in a tight loop.** Back off
  within the breaker cooldown, then retry once the window likely cleared.
* **Do not treat `transient_skip: false` as a provider ticket before
  checking your own fallback map.** An empty map ends the chain regardless
  of provider health.
* **Do not add the failing channel to its own fallback list.** A hop
  retrying the channel that just failed re-runs the same failure; the
  platform drops self-fallback entries server-side regardless.

## See also

* [Cross-channel fallback concept](/concepts/cross-channel-fallback) — the
  org-level chain and the campaign ladder this page triages, in full.
* [Fallback and cascade planes](/concepts/fallback-and-cascade-planes) —
  the five fallback planes and how to choose one.
* [Fallback chains guide](/guides/fallback-chains) — configure the chain
  per send and read the walk.
* [Verify fallback chains guide](/guides/verify-fallback-chains) — the
  platform-ordered SMS → voice → email chain.
* [Troubleshooting: message undelivered or failed](/troubleshooting/message-undelivered-failed) —
  the DLR-side triage when attempts reach a carrier and the receipt still
  reads failed.
* [Troubleshooting: messaging pre-send gates](/troubleshooting/messaging-pre-send-gates) —
  the pre-send gate chain that refuses before the router is consulted.
* [Troubleshooting: verify factor suite](/troubleshooting/verify-factor-suite) —
  the factor-side symptom map when the failure isn't the channel chain.
* [Error codes reference](/reference/error-codes) — the catalog this page
  triages.
