Skip to main content

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 and agent presence. 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:
Read details.reason first: 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.
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 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: 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