Skip to content
Yunus Shaikh
Engineering note · 2026-10-29

When an AI Agent Times Out After the Action

A tool call can commit a change while its reply is lost. A fictional invoice draft shows the idempotency contract and failure tests to require before automatic retries.

By Yunus Shaikh · Published · 5 min read

A timeout is an unknown outcome

A timeout tells you that the caller did not receive a reply in time. It does not prove that the server did nothing. A service can save a change and then lose the response on its way back. If an AI tool runner reports that call as failed and the agent submits it again, the second request may repeat the real-world effect.

A fictional invoice and a missing reply

Imagine a fictional bookkeeping assistant asked to create a supplier-bill draft for invoice INV-204. It sends POST /drafts. The server stores the draft and starts returning success, but the connection breaks before the tool runner receives the reply. The runner reports a timeout; the agent sees no result and tries the tool again. Without duplicate protection, two drafts may be created.

Put idempotency at the action boundary

HTTP defines an idempotent request by its intended effect: repeating the same request has the same intended effect as making it once. PUT, DELETE, and safe methods have idempotent semantics in RFC 9110. POST asks the resource to process the request according to its own rules, so the method alone does not make a create action safe to repeat. RFC 9110 cautions clients against automatically retrying a non-idempotent request unless they can know its semantics are safe to repeat or establish that the first attempt was not applied.

Keep the key tied to one intent

For a create action, the caller can assign one idempotency key to the user's intent before the first network attempt. Every retry of that intent reuses the same key and the same parameters. The server records the key with the action and, for a duplicate, returns the same resource or a meaningfully equivalent result. Reusing that key with a changed supplier or amount should produce a clear mismatch error, not silently turn the earlier request into a new one. A new, explicitly confirmed action gets a new key. Do not deduplicate only by payload: two identical draft requests can represent separate intentions. Business-specific duplicate-invoice checks still apply. For a simple synchronous endpoint, coordinate the key record and mutation atomically. A workflow spanning services needs explicit durable state and recovery. Document how long a key remains valid; after that window, reconcile before retrying.

A key protects only the boundary that honors it

If the action also sends an email, charges a payment method, or writes to another service, the first server's key does not automatically make that downstream effect idempotent. Pass the intent identifier to a downstream API that supports it; otherwise track and reconcile the downstream result before retrying.

Tests to pass before retrying a tool call

  1. Send one successful create request twice with the same key and parameters. Assert that there is one draft and both responses identify that same draft.
  2. Let the server commit a draft, then deliberately drop the response. Retry with the same key and parameters. Assert one draft exists and the retry reports the original outcome.
  3. Fail the request before the mutation commits, recover the service, and retry with the same key. Assert the operation completes once.
  4. Reuse a completed key with a changed supplier or amount. Assert a mismatch response and no second draft.
  5. Send two simultaneous requests with the same key through separate workers. Assert only one mutation wins and the other observes its result.
  6. Create two drafts with identical fields but different keys for two separately confirmed user actions. Assert that both exist; equal payloads alone do not prove equal intent.
  7. Simulate a timeout that lasts beyond the API's documented key-retention window. Assert the tool runner stops automatic resubmission and follows the service's status-lookup or reconciliation policy before making another create request.

Let the tool runner own the retry budget

Persist the intent key before the first attempt, keep the request parameters fixed for its retries, and put retry logic in one layer so an SDK, tool wrapper, and agent do not multiply attempts. Follow the endpoint's documented error contract: validation and permission failures need correction, not repetition; retry transient failures only when the action has duplicate protection. Use a maximum attempt count with exponential backoff and jitter. If a timeout leaves an unprotected action uncertain, look up its status or reconcile it before asking the model to create another action.

More writing →