> ## 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: Messenger channel not connected (409 MESSENGER_NOT_CONNECTED)

> Fix 409 MESSENGER_NOT_CONNECTED on Messenger channel and persona operations — the Page connection row is disconnected, so the operation refuses to touch the stored Page access token. Reconnect the Page through Channels → Messenger, then retry the write. Distinguish it from sibling code MESSENGER_TOKEN_UNREADABLE and from the not-configured family.

# Troubleshooting: Messenger channel not connected

A Messenger channel write or persona operation returns **409
`MESSENGER_NOT_CONNECTED`** when Meta is missing a valid connection
for the channel the request targets. On the Messenger lane it fires
on the persona calls — list, create, delete — whenever the stored
connection is marked disconnected or it carries no Page access token
at all. Refusing the call instead of sending it to Meta is
intentional: persona operations would fail at Meta with a far less
readable error if Orbit forwarded them without a token.

The 409 is a connection-state refusal, not a permission or
validation failure — no amount of request reshaping fixes it. Only
reconnecting the Page flips the condition the guard reads.

## When the 409 fires

| Operation                                                                 | Guarded by the connection check? | Refuses with              |
| ------------------------------------------------------------------------- | -------------------------------- | ------------------------- |
| `GET /api/v1/settings/channels/messenger/personas` (list)                 | Yes                              | `MESSENGER_NOT_CONNECTED` |
| `POST /api/v1/settings/channels/messenger/personas` (create)              | Yes                              | `MESSENGER_NOT_CONNECTED` |
| `DELETE /api/v1/settings/channels/messenger/personas/:personaId` (delete) | Yes                              | `MESSENGER_NOT_CONNECTED` |

The guard reads the connection row for the Messenger channel, and
any caller that resolves the stored Page access token routes through
it — so the list/create/delete surface is the whole persona
surface.

Read the sibling codes off the same refusal class — they route to
different fixes:

| Code                                                            | HTTP      | Meaning                                                                                                       | Fix                                                                                                                           |
| --------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `MESSENGER_NOT_CONNECTED`                                       | 409       | The connection row has no `connected` mark, or no stored Page access token                                    | Reconnect the Page (below)                                                                                                    |
| `MESSENGER_TOKEN_UNREADABLE`                                    | 409       | A token IS stored but server-side decryption failed — usually an encryption-key rotation or a corrupted value | Reconnect the Page too — the stored token is replaced and a fresh value encrypted                                             |
| `LINE_NOT_CONFIGURED` / `WHATSAPP_NOT_CONNECTED` style siblings | 412 / 410 | The sibling channels split the "not set up at all" refusal into a slightly different code                     | Route by the error envelope's `details.channel_id` (see below) — do not treat NOT\_CONNECTED and NOT\_CONFIGURED as one thing |

## Routing by the error envelope

The envelope carries the channel under a structured detail, so a
blocked writer routes readers by the exact code:

```json theme={null}
{
  "error": {
    "code": "MESSENGER_NOT_CONNECTED",
    "message": "Connect a Facebook Page to the Messenger channel before managing personas."
  },
  "details": {
    "channel_id": "messenger"
  },
  "meta": {
    "request_id": "req_def456",
    "timestamp": "2026-09-27T10:14:03Z"
  }
}
```

For Messenger both "nothing stored" and "stored but disconnected"
collapse into `MESSENGER_NOT_CONNECTED`, and the fix is identical
either way — so this particular code needs no channel branch.
Sibling channels DO split it — LINE's `LINE_NOT_CONFIGURED` is a
settings-completeness refusal, where a raw token/email config is
absent, and other channels branch `NOT_CONNECTED` from
`CONNECTION_INVALID`. Routing code should read
`details.channel_id` before lumping every refusal onto
"reconnect."

## Diagnose: confirm the channel is really disconnected

Check either surface before touching OAuth — some disconnected
reports turn out to be `MESSENGER_TOKEN_UNREADABLE`, which needs the
same reconnect but starts from a different envelope, so confirm the
code you actually got.

1. **Dashboard surface.** Open **Settings → Channels → Messenger**.
   The status badge reports `disconnected` (no `connected` mark) or
   `expired` (token present but past its rotation window) — either
   routes into the reconnect flow below.
2. **API surface.** Read the channel-status surface:

   ```bash theme={null}
   curl "https://api.orbit.devotel.io/api/v1/settings/channels/messenger/status" \
     -H "X-API-Key: $ORBIT_API_KEY"
   ```

   For the Meta-adjacent channels the richer connected/token view
   (valid / expiring\_soon / expired) rides on this endpoint. A
   disconnected channel reports the connection as absent; a
   `MESSENGER_TOKEN_UNREADABLE`-class channel still reports a token
   but the endpoint cannot vouch for its decryptability.

## Fix: flip the channel back to connected

Two self-serve reconnect paths replace the stored token with a
fresh, correctly scoped one. Pick OAuth (recommended) or the
scope-repair flow when your company's Meta app-review approves fewer
scopes than the Page picker requests.

### Path A — OAuth reconnect with the Page picker

The full ordering is on the
[Messenger channel page](/channels/messenger#onboarding); condensed
for this recovery:

1. In **Settings → Channels → Messenger**, click **Reconnect**.
2. Meta's OAuth dialog re-opens; re-select the Page you were using
   (the dialog lists Pages you administer).
3. Outbound sends and persona operations resume as soon as the
   stored token is replaced.

### Path B — re-attach the required Meta scopes

If the OAuth dialog completes but the channel still refuses with
the 409, the scopes granted to the Meta app were narrowed. Re-run
the dialog with both scopes checked (per the channel page's
[required-scopes](/channels/messenger#required-scopes) list):

| Scope             | Why the personas guard needs it                                                    |
| ----------------- | ---------------------------------------------------------------------------------- |
| `pages_messaging` | Meta rejects persona operations without it, so Orbit refuses earlier               |
| `pages_show_list` | Without it the picker surfaces no Pages and the reconnect stalls inside the dialog |

Re-running the dialog with both scopes re-selected flips the stored
row back and the 409 stops.

## Confirm the flip

The condition the guard checks is the connection row having
`connected` plus a stored Page access token. After either path,
re-read the channel status — a healthy reconnect reports the
channel as connected with the token valid (or `expiring_soon`; that
still satisfies the guard — the 409 fires only on absent).

Then retry the refused personas call once. Do not loop it against
the 409: the refusal is deterministic, and a loop re-reads the same
guard on every attempt.

## Distinguish from the not-configured family

Sibling channels split "nothing has ever been set up" into a
`*_NOT_CONFIGURED` code (412) instead of `*_NOT_CONNECTED` (409).
The distinction is bookkeeping, not severity, and it matters only
to routing code: the `NOT_CONFIGURED` family means the tenant's
settings payload never carried that channel at all (self-serve:
store the credential), while `NOT_CONNECTED` means a channel row
exists but the connection guard reads it as disconnected
(self-serve: reconnect). Route on `details.channel_id` before
picking a remediation, and never treat a `NOT_CONFIGURED` as a
reason to re-OAuth something that was never set up.

## When NOT to retry

* **Do not retry the same personas request.** Every retry hits the
  same guard and returns the same 409 until a reconnect lands.
* **Do not retry with a different persona name or avatar.** The
  refusal fires before any persona data is read; nothing in your
  request body can steer around it.
* **Do not create new personas while disconnected.** The guard
  refuses list/create/delete identically; the read side also 409s.

## Escalation checklist

Open a ticket when the channel status reports connected, the OAuth
dialog completed with both scopes, and the personas call still
409s. Include:

* The `request_id` from `meta` on the 409.
* The exact endpoint you were calling (list, create, or delete).
* The channel status snapshot (dashboard badge or the API read).
* The code you received — `MESSENGER_NOT_CONNECTED` vs
  `MESSENGER_TOKEN_UNREADABLE` splits the follow-up.

## See also

* [Messenger channel page](/channels/messenger) — full onboarding
  ordering, required scopes, persona limits, and the error table this
  code is a row of
* [Troubleshooting: WhatsApp connection and
  re-authentication](/troubleshooting/whatsapp-connection) — the
  sibling page where the connected / token-unreadable split sits at
  `NOT_CONNECTED` vs `CONNECTION_INVALID` instead of 409 on both
* [Troubleshooting: Messenger 24-hour window
  closed](/troubleshooting/messenger-window-closed) — the send-side
  sibling in the Messenger error family
* [Error Code Reference](/reference/error-codes) — every code in the
  messaging family, in one table
* [Troubleshooting hub](/reference/troubleshooting-hub) — every
  runbook in one index
