Designing escalation paths for AI agents that get stuck

An agent can spend six minutes calling the same search tool with slightly different queries and still finish with, “I couldn’t find the answer.” The user sees one sentence. The system has burned tokens, delayed the task, and left nobody responsible for the next step.

“Escalate when uncertain” sounds like a safety policy, but it is not yet a workflow. Who receives the case? What evidence goes with it? Does the agent stop, or keep running while the human investigates? Can the human resume the task? What happens if the queue is closed or the reviewer rejects the proposal?

An escalation path is the designed transition from autonomous work to a person, a deterministic fallback, or a controlled stop. It is one of the core parts of an agent product, not a generic error message at the end of a loop. This article explains how to detect that an agent is stuck, select the right destination, preserve useful context, and return to the workflow safely.

Stuck, uncertain, and blocked are different states

An agent can stop making progress for several reasons:

  • Uncertain: the task has multiple plausible answers or insufficient evidence.
  • Blocked: a required tool, permission, or external dependency is unavailable.
  • Looping: the agent repeats a tool call or cycles between plans without new evidence.
  • Out of budget: the request reached a time, token, or cost limit.
  • Policy constrained: the action requires approval or is not permitted.
  • Human-dependent: the system needs a fact or judgment only the user can provide.
  • Failed: a tool or provider returned a terminal error.

These states call for different next steps. Uncertainty may be resolved by asking the user one clarifying question. A tool outage may be handled by a queue and retry later. A prohibited action should be stopped, not routed to another agent that might try again. A loop needs a termination rule before a person is paged.

The first design move is a typed outcome. Do not reduce all non-success results to needs_human. That hides the reason and creates a queue full of cases the human cannot act on.

1. Detect stalled progress from behavior, not just iteration count

A hard maximum number of model turns is necessary, but it is a coarse control. An agent that completes a task in eight steps should not be escalated simply because another task needed two. Conversely, an agent that repeats the same failed call three times may already be stuck.

Track progress signals such as:

  • whether a tool result added new information;
  • whether the state or plan changed meaningfully;
  • whether the same normalized tool call was repeated;
  • whether the agent is alternating between the same two tools;
  • whether an unresolved required field remains missing;
  • whether the time, token, or spend budget is nearly exhausted;
  • whether the current step is waiting on a person or a dependency.

Define a task-specific progress predicate. For an order lookup, a successful tool result containing the order status is progress. Repeating the lookup with the same order ID and same filters is not. For research, a new source may be progress even if it does not answer the final question. The predicate belongs in the orchestration layer, not in the model’s self-description.

Here is a simple loop detector. The fingerprint must be derived from normalized tool arguments and relevant state, not raw prompt text:

from collections import Counter
from hashlib import sha256
import json

def call_fingerprint(tool_name: str, arguments: dict, state_version: int) -> str:
    canonical = json.dumps(arguments, sort_keys=True, separators=(",", ":"))
    material = f"{tool_name}|{state_version}|{canonical}"
    return sha256(material.encode("utf-8")).hexdigest()

def should_escalate(run, call_history: list[str], max_turns: int) -> str | None:
    if run.turn_count >= max_turns:
        return "turn_budget_exhausted"
    if run.remaining_spend <= 0:
        return "spend_budget_exhausted"
    if run.remaining_time_seconds <= 0:
        return "deadline_exceeded"
    counts = Counter(call_history)
    if any(count >= run.repeat_limit for count in counts.values()):
        return "repeated_tool_call"
    if not run.progress_since_last_checkpoint:
        return "no_progress"
    return None

The values max_turns and repeat_limit are product policy, not universal defaults. A read-only document search and a payment workflow need different budgets. The detector should return a reason code that the next step can route.

2. Choose the destination by failure class

Escalation does not always mean “send to a human.” Build a routing table for the conditions your agent can encounter.

Condition Suitable next step Avoid
Missing user-specific detail Ask the user a targeted question Guessing from weak context
Repeated retrieval failure Search alternate approved source or route to a specialist Repeating the same query indefinitely
Permission denied Explain the boundary or request authorized access Retrying with a broader service account
Tool outage Persist task and queue a bounded retry Keeping the web request open indefinitely
High-impact action needs approval Create a review item with exact proposal Letting another agent auto-approve
Conflicting authoritative sources Human review or explicitly surface conflict Averaging the two values
Policy-prohibited request Stop and provide an allowed alternative Escalating until someone bypasses policy
Unknown external outcome Reconcile with the system of record Repeating a potentially completed write

The route should be deterministic given the reason and task policy. An agent can recommend escalation, but it should not be able to choose a destination that bypasses the authorization rules.

3. Build a handoff package, not a transcript dump

A person cannot help if the escalation contains only “agent failed.” They also should not have to read a 40,000-token conversation and reconstruct the state by hand.

Create a compact handoff package with:

  • the user’s goal in the user’s own words;
  • a structured summary of what has been verified;
  • the unresolved question or decision;
  • relevant source references and versions;
  • tool calls attempted, outcomes, and error classes;
  • the exact proposed action, if any;
  • policy and authorization results;
  • whether any side effect may already have happened;
  • the required next action and owner;
  • a link to the restricted trace for authorized investigation.

Separate facts from model interpretation. For example:

{
  "task_id": "task_4f31",
  "reason": "conflicting_order_state",
  "user_goal": "Change delivery to the office address",
  "verified_facts": [
    {"source": "orders-api", "version": "17", "fact": "shipment is in transit"},
    {"source": "address-book", "version": "8", "fact": "office address is active"}
  ],
  "unresolved": "Carrier API does not permit address changes after dispatch",
  "proposed_action": null,
  "side_effect_status": "none",
  "next_step": "contact_carrier_support"
}

The handoff should not repeat secrets or unrelated customer content. Keep raw conversation and tool payloads behind the same access control as the source data. A summary is convenient; it is not automatically safe to distribute widely.

4. Stop the agent before it competes with the reviewer

Once a task is escalated, define whether the agent is paused, terminated, or allowed to continue on a separate read-only path. For a side-effecting action, the safe default is to stop the agent from executing further writes while the review is pending.

Use a durable task state such as:

running → escalated → awaiting_human → approved/rejected/needs_info
                                      ↓
                                  resuming

The transition to escalated should persist before the reviewer is notified. Otherwise, a notification failure can leave the agent running while the UI claims a human is taking over.

If the agent is waiting for user clarification, it may be appropriate to keep the task open but suspend model calls until new information arrives. If a provider outage caused escalation, store a retry schedule and a deadline. If a human is reviewing a write proposal, disable the corresponding live tool until approval is recorded.

Do not create two owners for one operation. An agent and a human should not both be able to apply the same refund, publish the same document, or close the same ticket. Bind the pending operation to one state machine and use idempotency keys for any subsequent execution.

5. Make the reviewer’s job finite and actionable

Human queues can become dumping grounds. A useful escalation item answers three questions at the top:

  1. What decision is needed?
  2. Why could the agent not safely decide it?
  3. What evidence should the reviewer inspect?

Offer defined actions such as approve this exact proposal, edit the parameters, ask the user, reject with a reason, or return the task to the agent with additional context. Avoid a generic “continue” button that does not tell the system which state to resume.

For approval, bind the decision to the exact proposal hash and current resource version. If the record changes while the reviewer is reading, invalidate the approval and refresh the preview. If the reviewer edits a parameter, create a new approved operation rather than mutating the original proposal silently.

Prioritize queues by impact and deadline, not only arrival order. A low-risk tag suggestion should not bury a time-sensitive account lock review. Show how long an item has waited, who owns it, and what happens if the service-level target is missed.

6. Resume from durable state, not an ambiguous transcript

After a human responds, the agent needs structured state: the reviewer’s decision, any edits, the supporting note, and the current resource version. Do not just append “human said okay” to the chat and let the model infer what was approved.

The resume path should:

  • verify the reviewer had authority for this action;
  • confirm that approval refers to the current proposal;
  • re-read and revalidate the resource;
  • pass only the necessary new information to the agent;
  • keep the original operation ID when continuing the same operation;
  • create a new operation ID when the human changes the intended action;
  • record the final result and reviewer identity in the audit trail.

Long-running workflows benefit from durable execution because they can pause for minutes or days and resume after a worker restart. Temporal’s AI documentation describes workflows that wait for human approval and continue from recorded execution state. The exact platform is a choice; the important property is that a pause does not depend on a process remaining in memory. (Temporal, AI)

7. Prevent escalation loops between agents and humans

Multi-agent designs can create a handoff cycle: triage sends to billing, billing sends back to triage, and triage sends to billing again. Human review can loop too if reviewers return a task without adding a reason or the agent receives the same context unchanged.

Guard against loops with:

  • a bounded count of handoffs per task;
  • a record of prior destinations and reasons;
  • a requirement that a return from human review includes a changed field, decision, or new evidence;
  • a timeout after which the task is reassigned or closed;
  • a terminal “cannot proceed” state with a user-facing explanation.

Detect the cycle using task state, not by asking the model whether it thinks it is looping. If the same task enters the same agent with the same state version and no new information, the orchestrator should stop or choose a different route.

8. Escalate before budget exhaustion

If a task is already near its spend or deadline limit, waiting until the last token to escalate leaves no room to produce a useful handoff. Reserve enough budget for a compact summary, durable state write, and user notification.

Budget per logical task, not just per model call. Include:

  • repeated model turns;
  • retries after transient errors;
  • retrieval and tool costs;
  • summarization for the handoff;
  • time spent waiting for a reviewer;
  • any compensation or reconciliation step.

Set a checkpoint before the hard limit. At that point, stop exploratory tool calls and create the handoff package. The user should not be charged for an endless loop that never reaches a useful outcome.

9. Design clear user-facing escalation states

The user should understand whether the agent is waiting, asking a question, handing the task to a person, or unable to continue. Avoid vague copy like “Something went wrong” when the next step is known.

Examples:

  • “I found two matching accounts. Choose the correct one before I continue.”
  • “This change needs approval from a billing administrator. I’ve saved the draft and sent it for review.”
  • “The carrier may have processed the request, but I haven’t received confirmation. I’m checking the shipment status before retrying.”
  • “I can’t change an address after dispatch. I can show the carrier contact details or ask support to investigate.”

Preserve the user’s draft and show a reference number for follow-up. If the task is queued, provide a status location and a realistic notification expectation. Do not promise a human response time unless the team can meet it.

For accessibility, announce state changes in text, keep focus predictable, and ensure the escalation actions work with keyboard navigation and screen readers. The user should not need to infer a handoff from a color change or animation.

10. Measure the escalation path

Escalation quality is more than the percentage of tasks routed to humans. Track:

  • escalation rate by reason and action type;
  • time to first human response and time to resolution;
  • percentage of handoffs with enough context to act;
  • reviewer edits, approvals, rejections, and requests for more information;
  • repeated handoffs or re-opened tasks;
  • cases where the agent should have escalated but did not;
  • cost accumulated before escalation;
  • side effects executed before a required review.

Review both false escalation and missed escalation. If the agent routes every difficult question to a person, users get a slow wrapper around the existing queue. If it rarely escalates, it may be hiding uncertainty behind fluent prose. Sample decisions from both sides of the boundary and label them with domain experts.

OpenAI’s Agents SDK documents handoffs as traceable spans alongside model calls and tool calls. That makes the transition observable, but observability alone does not determine whether the destination or timing was correct. The product still needs a rubric for which cases should hand off and an owner for the queue. (OpenAI Agents SDK, tracing)

Treat the queue as part of the product architecture

An escalation queue needs more than a database table with status = pending. Define who owns each queue, which cases qualify, how they are prioritized, and what happens when no reviewer is available.

Useful queue properties include:

  • priority based on consequence, deadline, and user impact;
  • fair scheduling so one tenant or noisy workflow cannot starve others;
  • an explicit assignment or claim lease so two reviewers do not process the same item;
  • a service-level target and an alert before it is missed;
  • a dead-letter or “needs policy owner” route for cases no frontline reviewer can resolve;
  • durable state across deploys and worker restarts;
  • a clear way to return a case to the agent with added information.

Do not send every problem to the general support queue. A provider outage belongs with operations; an access denial belongs with the tenant administrator; a policy ambiguity belongs with the policy owner. The right destination shortens resolution and prevents support from becoming a human retry mechanism for work the system cannot safely complete.

Queue capacity is a product limit. If escalation volume doubles after a prompt change, the system may have degraded even if the agent’s “completion” metric is stable. Set alerts on queue age and unresolved count, and define what the product does when the review capacity is exhausted: pause automation, narrow the feature, or present a wait state. Do not keep accepting tasks and silently accumulating a backlog.

Reduce approval fatigue without hiding risk

Reviewers become less effective when every item looks equally urgent. A queue that asks for approval on harmless formatting and on a high-value refund trains people to click through.

Separate review classes by consequence and information need. Low-risk proposals can be grouped into a batch with a clear summary and per-item inspection. High-impact actions should remain individually visible with the exact target, parameters, and supporting evidence. Anything outside the reviewer’s authority should not appear as an actionable approval button.

Measure whether reviewers inspect evidence, edit proposals, reject actions, or ask for more context. A very high approval rate is not automatically evidence of model quality; it may reflect a poor interface or time pressure. Periodically insert known-good and known-bad review cases into training or quality checks, but do not use deceptive tests in a way that undermines reviewer trust.

Use review decisions to improve the workflow. If the same correction repeats, update the policy, source data, tool contract, or prompt and add the case to evaluation. Do not teach the agent to mimic reviewer clicks without understanding why the decision was made.

Keep handoff data scoped and access-controlled

Escalation often broadens access: a model had a narrow data view, but the case is now visible to a larger group of staff. The handoff should carry only the content required for that reviewer to act, and the queue should enforce role-based access to the underlying records.

Avoid copying full prompts, documents, or entire conversation histories into the queue item. Store references to source records and retrieve them through the reviewer’s authorized session. If a reviewer needs a short excerpt, redact unrelated personal data and preserve provenance so the excerpt is not mistaken for a complete record.

The audit log should distinguish the agent’s service identity, the requesting user, and the human reviewer. Those are different actors. A reviewer’s approval should not make the agent’s original proposal appear to have been authored by the human, and a service account should not obscure which user initiated the task.

Decide what happens when no human is available

An escalation that waits forever is a hidden failure state. Define a timeout or expiry policy for every human-dependent task. The correct result depends on the action: a draft can remain pending, a time-sensitive booking may expire, and a permission change should stay unapplied until an authorized reviewer acts.

The system may reassign to another qualified queue, notify the requester, or close the task with a clear explanation. It should not silently lower the approval requirement because the queue is slow. If the business chooses automatic action after a deadline, that is a separate policy that needs explicit authorization and evaluation.

Store the deadline and the reason for it in the task record. Notify the user before an action expires when that is useful. When a reviewer returns later, show whether the underlying resource has changed; an old proposal may no longer be valid. The resumed workflow should re-read current state and renew any approval that no longer matches the action.

Make the handoff destination part of the tool boundary

Agents can choose to call tools, but they should not be able to invent queue names, assign cases to arbitrary employees, or select a reviewer who lacks authority. Map escalation reasons to destinations in application code.

For example, billing_write_requires_approval can route to a billing-admin queue, while source_conflict routes to a domain specialist. The model may provide a short explanation, but the server determines the queue based on a controlled reason code, tenant, action type, and current workload policy. Validate any reviewer assignment at the moment the approval is recorded.

This prevents a prompt injection from steering a sensitive task to an unauthorized person and gives operations a stable place to measure queue health. It also makes the routing policy testable without running the model.

Keep the reason codes versioned with the workflow. If a routing category changes meaning, dashboards and historical comparisons can become misleading. Record both the stable code and a human-readable explanation, and review the mapping when a new tool or escalation destination is introduced.

Deprecate old codes deliberately rather than reusing them for a new policy. Preserve old mappings for historical review.

This makes incident reports comparable across releases and avoids relabeling past cases.

It also gives support a stable language for explaining why the task changed owners. Keep that wording concise and user-centered. Avoid blaming the user. Make the next step clear. And easy to find.

A practical escalation policy

For each agent task, define:

  • a progress signal and loop detector;
  • a turn, time, and spend budget;
  • terminal reasons and their destinations;
  • which tools are disabled after escalation;
  • a minimal handoff schema with source provenance;
  • the human actions available and their authorization requirements;
  • whether the task can resume, and from what durable state;
  • an expiration or reassignment policy;
  • metrics for false and successful escalations.

Escalation is not an admission that the agent failed. It is a designed outcome for cases where automation should stop. A good agent knows when it lacks evidence, when a policy boundary needs a person, and when repeating a tool call will not help. A good product makes that handoff understandable, actionable, and safe to resume.

Post a Comment

Previous Post Next Post