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.