Yutong Wang
All notes

Note · October 2026

When an agent times out after it already succeeded

A tool call can time out after the action has already happened. If your agent retries automatically, you get two tickets, two emails, or two payments. Test this before you turn retries on.

The failure

An internal support agent calls create_ticket. The ticketing API creates the ticket, but the response is slow and the agent's tool call times out. From the agent's side, the call failed. It retries, and now there are two tickets.

The outcome wasn't a failure. It was unknown. Most agent frameworks only have two states, success and error, so "unknown" gets treated as "error", and the retry does the damage.

Three patterns that fix it

1. Reuse one idempotency key for every attempt

Generate the key once, when the agent decides to act, not inside the tool call. Every retry of that action sends the same key, so the server can return the first result instead of acting twice.

key = f"ticket:{conversation_id}:{step_id}"
create_ticket(payload, idempotency_key=key)   # retries send the same key

2. Check before you retry

If the API doesn't support idempotency keys, look before acting again: search for a ticket with the same reference or the same content in the last few minutes. Only retry when the check confirms nothing was created.

3. Make "unknown" a real state

Give tool results three outcomes: succeeded, failed, and unknown. Unknown pauses the step, runs the check above, and escalates to a person if the check is inconclusive. It is never silently turned into a retry.

How to test it

Wrap the tool in a test double that performs the action and then raises a timeout. Run the agent and assert that exactly one record exists. It's a ten-line test, and it catches a bug that otherwise shows up first in production, as a customer with two invoices.


I design agents that write to real systems: tickets, CRMs, documents, files. If yours does too and you want a second pair of eyes on its retry behavior, I'm happy to look.