Claude Certified Architect - Foundations study materials
Claude Certified Architect - Foundations study resources

Agent Loops Forever on a Flaky Inventory API

Claude Certified Architect - Foundations · Put your knowledge to work in context.

All case studies

Agent Loops Forever on a Flaky Inventory API

A procurement agent uses Claude with tools check_inventory(sku), get_price(sku), and place_order(sku, qty). The check_inventory backend has intermittent 500 errors (~8% of calls) and occasionally returns an empty body with a 200 status. In a recent incident, the agent called check_inventory 41 times for one SKU over 9 minutes, then placed an order for quantity 500 when the user asked for 5. There is no max-iteration cap. Tool errors are passed back to the model as raw JSON {"error": "internal server error"}. The agent runs unattended overnight.

1. Identify the failure modes that produced both the 41-call loop and the wrong quantity, and explain how they compound.

Read the worked answer

Two distinct failure modes compound here.

The loop: the agent receives a raw error string with no guidance on whether the error is retryable or terminal. An 8% transient error rate means retries often succeed, which reinforces retrying - but the model has no stopping rule. With no max-iteration cap and an empty-200 case that looks like "no data" rather than "error," the model can't distinguish "try again" from "give up." It retries indefinitely.

The wrong quantity: after 41 calls the context is stuffed with error JSON and partial results. The original instruction (qty 5) is now a small signal among a large volume of noise - a classic lost-in-the-middle/context-dilution effect. The model may also have latched onto a number from a tool payload or misread a partial response. Quantity 500 for a request of 5 is a 100x error that no downstream check caught because place_order has no validation.

They compound because the loop both burns the time budget and pollutes context, making the eventual action less reliable. The empty-200 case is the worst offender: it's silently misclassified as valid data, so the model may proceed on garbage.

2. Design the guardrails - at the tool layer, the orchestration layer, and the prompt layer - that would have prevented this incident.

Read the worked answer

Defense in depth, because each layer alone is insufficient:

Tool layer (most reliable):

  • Return structured, typed errors: {status: "retryable"|"terminal", code, message, retry_after} so the model knows whether retrying is sensible.
  • Treat empty-200 as an explicit error, not empty data.
  • Add server-side retry with backoff for transient 500s so the model rarely sees them.
  • Validate place_order server-side: reject quantities outside a sane band or above a per-order cap, and require the quantity to echo a value the user actually requested. This alone blocks the 500-unit order.

Orchestration layer:

  • Hard max-iteration cap (e.g., 8) and a wall-clock timeout; on breach, halt and escalate to a human queue rather than acting.
  • Cap consecutive identical tool calls (e.g., 3) and break the loop.
  • Require human confirmation for orders above a threshold or any order after a degraded tool session.

Prompt layer:

  • State the retry policy explicitly ("retry a retryable error at most twice; on terminal error or repeated failure, stop and report").
  • Restate the user's requested quantity in the order step and require the agent to confirm it matches before calling place_order.

Prompt alone wouldn't have held - the tool-layer validation is the backstop that makes the 100x error impossible regardless of model behavior.