> ## 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: 409 NO_ELIGIBLE_AGENT on route to queue

> Decode the 409 NO_ELIGIBLE_AGENT refusal when you route a ticket through a queue, separate it from the automatic-assign overflow reasons, read the queue and agent state that caused it, and fix the owning control.

# Troubleshooting: 409 NO\_ELIGIBLE\_AGENT on route to queue

Routing an unassigned ticket through a queue (**Route to queue** on the ticket, or `POST /api/v1/inbox/tickets/{id}/route-through-queue`) asks the queue to pick an agent for the ticket right now: a queue member whose skills cover the queue's required set, whose presence is `available`, and who has headroom under their per-channel concurrency cap. `409 NO_ELIGIBLE_AGENT` means the queue exists but, at that instant, nobody in it passed those gates. The ticket stays unassigned; nothing about the conversation or the ticket changes. This page decodes the refusal, separates it from the automatic-assign overflow reasons, and walks the checks that fix it.

Everything below is tenant-owned configuration on [digital queues](https://docs.orbit.devotel.io/inbox/digital-queues.md) and [agent presence](https://docs.orbit.devotel.io/concepts/agent-presence-lifecycle.md). A queue never touches provider or carrier behaviour, so the fix always lives in your workspace settings.

## The 409 envelope and what it means

The refusal is a `409` with `error.code` `NO_ELIGIBLE_AGENT`, and `error.details` carries the queue and the reason:

```json theme={null}
{
  "error": {
    "code": "NO_ELIGIBLE_AGENT",
    "message": "ACD queue has no eligible blended agent available right now",
    "status": 409,
    "details": {
      "queue_id": "a1b2c3d4-…-queue-id",
      "reason": "no_eligible_agent"
    }
  },
  "meta": { "request_id": "req_01J…", "timestamp": "2026-11-03T09:14:02.118Z" }
}
```

Read `details.reason` first:

| Reason | Meaning |
| - | - |
| `no_members` | The queue has no members at all — nobody is enrolled to take its work. |
| `no_eligible_agent` | The queue has members, but none of them passed every gate at that instant: each member was either not `available` on presence, or did not cover the required skills at the [minimum skill level](https://docs.orbit.devotel.io/inbox/digital-queues.md), or (with capacity-aware routing on) was over the overload threshold. |

Two adjacent refusals on the same call are different codes, not this one: `422 AGENT_CHANNEL_CAP_EXCEEDED` means members matched skills and presence but every one of them sat at their per-channel concurrency cap for the ticket's channel (a cap is a deliberate ceiling, so the cap case refuses rather than over-assigns); `409 ALREADY_ASSIGNED` means the ticket already has an owner and must be reassigned explicitly instead.

The refusal is a routing decision, not a failure of the request itself: the queue was found and evaluated, and the answer was "nobody qualifies right now."

## Manual route vs automatic-assign overflow: different triggers, same root cause

A queue meets the same eligibility gates in two places, and the refusals read differently:

* **Manual route-to-queue** (this page) is synchronous. It evaluates the gates once, at request time, and refuses with `409 NO_ELIGIBLE_AGENT` when the pool is empty. There is no waiting set behind it: a manual route either assigns immediately or returns the 409.
* **Automatic assignment** of inbound conversations is patient. When no eligible agent is free, the conversation joins the queue's waiting set with an SLA deadline and keeps re-evaluating; it only sheds per the queue's `overflow_action`, and the shed reasons on that path are `queue_full` (the waiting set reached max queue depth), `sla_breached` (the SLA deadline passed with still no eligible agent), and `no_skilled_agents` (nobody in the workspace holds the required skills, so waiting is pointless and the queue sheds immediately) — the overflow model is on [Digital queues](https://docs.orbit.devotel.io/inbox/digital-queues.md#how-routing-behaves).

The shared root cause is the same on both paths: at the moment of evaluation, no agent combines queue membership, skill coverage, `available` presence, and per-channel headroom. The difference is trigger shape — the manual path reports the empty pool instantly as a 409, while the automatic path holds first and only names the condition as an overflow reason when holding can no longer help. So `no_skilled_agents` on the automatic path and `NO_ELIGIBLE_AGENT` on the manual path are the same underlying state seen from two surfaces, and the fix table below applies to both.

## Read-state checks

Work these in order; each names where to look:

1. **Required skills and minimum skill level.** Open **Inbox → Settings → Digital queues**, edit the queue from `details.queue_id`, and read **Required skills** and **Minimum skill level**. A member is eligible only when their profile covers every required skill at or above the minimum level; a single narrow skill nobody holds empties the pool. An empty required-skills list means any member is eligible.
2. **Member enrollment.** On the same form, confirm the queue actually has members, and that the agents you expect are listed with membership state `available`. A queue whose roster is empty returns `no_members` before any skill check runs.
3. **Presence.** Eligibility requires live `available` presence: an agent who is `offline`, `paused` for the day, or on a voice `busy` / `wrapup` leg is skipped. Read the team view (or [Presence federation states](https://docs.orbit.devotel.io/troubleshooting/presence-federation-states.md) when a presence badge looks wrong) and confirm at least one member is `available` at the moment you route.
4. **Per-channel concurrency caps.** Under **Settings → Team → Channel concurrency caps**, each agent has a ceiling per channel. Members who matched skills and presence but sit at the cap for the ticket's channel produce `422 AGENT_CHANNEL_CAP_EXCEEDED` rather than this 409 — if that is the code you actually see, the fix is cap headroom, not skills.
5. **Overflow action.** `overflow_action` does not change this 409 — it governs what the *automatic* path does with a conversation the queue cannot place (`routing_rules`, `overflow_queue`, `auto_responder`, `hold`). You will want it set for the waiting-set side of the same root cause, but no overflow setting turns a manual-route 409 into an assignment.
6. **Live stats cross-check.** `GET /api/v1/inbox/digital-queues/stats` reads the same state back per queue: `backlog` above zero with `agents_available` at zero is the skill/presence shape of this refusal; a positive `agents_available` with a stuck backlog points at the per-channel cap instead.

## Fixes

Match the cause you found to its fix:

| Cause | Fix |
| - | - |
| Required skills too narrow for the roster | Broaden or clear **Required skills** on the queue — an empty list makes every member eligible; alternatively add the missing skill to the agents who should take this work |
| Minimum skill level above the roster's levels | Lower **Minimum skill level**, or raise the agents' recorded levels for those skills |
| No members, or no member present | Enroll eligible agents on the queue with membership `available`, and confirm at least one member's live presence is `available` at routing time |
| Every eligible member at their per-channel cap (the `422 AGENT_CHANNEL_CAP_EXCEEDED` sibling) | Raise the per-channel cap under **Settings → Team → Channel concurrency caps**, or close open work to free headroom |
| Recurring refusals on the automatic path | Set `overflow_action` to `overflow_queue` (tiered escalation) or `routing_rules` so unplaceable conversations move on instead of holding |

Every fix except waiting on presence is a queue or team setting you own; none requires a provider change.

## Do not blind-retry

An immediate repeat of the same route call evaluates the same state and returns the same 409 — the refusal is deterministic, not a transient fault, so a retry loop only repeats it. Retry is correct after a state change from the table above lands (an agent flips to `available`, a cap is raised, a skill list is widened); when you do retry, the assignment is atomic, so a route that loses a race against a concurrent assign returns `409 ALREADY_ASSIGNED` — read the ticket's assignee before routing again rather than re-firing.

***

## See also

* [Digital queues](https://docs.orbit.devotel.io/inbox/digital-queues.md) — skill match, SLA, overflow actions, and the live stats endpoints
* [Inbox assignment and auto-close](https://docs.orbit.devotel.io/troubleshooting/inbox-assignment-and-auto-close.md) — the inbound `pending`-lane decoder on the automatic path
* [Agent presence lifecycle](https://docs.orbit.devotel.io/concepts/agent-presence-lifecycle.md) — the presence states every eligibility gate reads


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.