All articles
5 min readBy Ming Shin

The Agent Ran Twice: Duplicate Actions And The Retry Problem

Timeouts, retries, and parallel workers make agents repeat real actions. How to make every side effect safe to run twice.

AI OperationsProductionWorkflow Automation

The first incident usually arrives as a customer complaint rather than an alert: two invoices for the same order, two copies of the same reply in someone’s inbox, two tickets created for one issue and resolved by two different people.

Nobody wrote a bug. The agent did exactly what it was asked to do twice, because the first attempt looked like it had failed. This is the most under-planned class of failure in agentic systems, and it is not a model problem — it is a distributed systems problem that arrives the moment a model can change something outside its own process.

Why the double run is structural

An agent that only reads is easy to make safe: run it again, same answer. An agent that writes is a different animal, because writing is not a question you can ask twice for free.

The double run comes from four places; most deployments have at least two.

Timeouts with ambiguous outcomes. The tool call to create the invoice went out. The HTTP request timed out after thirty seconds. The model, or your orchestration layer, sees a timeout and does the reasonable-looking thing: try again. But the first invoice was created. The timeout described your knowledge of the outcome, not the outcome itself.

Retries inside the model loop. Agent frameworks retry tool calls on error by default, and a model reasoning about its own failure will often re-issue the same action because the last attempt “did not work.” Together, on a non-idempotent endpoint, they multiply.

Parallelism. Fanning an agent out across twenty items is the standard way to make it finish in reasonable time. Two workers can reach the same underlying record — the same customer, the same order — and neither one knows the other exists. Deduplication at the queue level does not help when the duplicate is created by two different reasoning paths.

Human re-approval. The reviewer rejects an output, the agent regenerates, the reviewer approves the second version — and the first version’s side effect was already committed by an over-eager step before review. The approval workflow thought it was gating a single action. It was gating one of two.

The window nobody watches

Draw the sequence and the real problem becomes visible. There is a gap between the moment the side effect is committed by the downstream system and the moment your agent learns that it was. Everything in that gap is invisible to the model, and the model fills the invisibility with a guess.

A successful charge followed by a dropped response looks identical, from inside the agent, to a request that never arrived. The correct behaviour in both cases is “this action may or may not have happened,” and that is a state most agents are not designed to hold. They collapse it into a boolean — did the call fail or not — and proceed.

The consequence is worse than a duplicate: agents in that state often take a compensating action that is also wrong — cancelling an order that was never placed, or refunding a payment that was already refunded by a human an hour earlier.

Idempotency keys belong at the intent, not the tool

The standard fix is an idempotency key, and the standard mistake is putting it in the wrong place — generated at the point the tool is called. A key generated at the call site is a fresh key on every retry, which makes it useless. It guards nothing.

The key has to be derived from the intent: what this run is trying to do, for which entity, in which period. Something like the order id plus the action name, or the task id plus the target record. Same intent, same key, no matter how many times the model decides to try. Different intent, different key, so a genuinely new request is not silently swallowed by a stale one.

That requires the key to be minted before the model starts reasoning about the task, not after it decides to act — which means the orchestration layer owns it, not the prompt. Getting this right is a schema decision, and it belongs in the same conversation as what the agent is allowed to touch. Each write endpoint needs its own intent-derived key and each downstream API needs to honour it — half of them will not, and those are the ones you wrap.

Read back before you write again

For the systems that cannot take an idempotency key — most internal admin tools, and anything built before anyone thought about this — the fallback is a read before write.

Before committing an action, the agent checks for the effect: does this invoice exist, is this ticket already open, was this message already sent. It is one indexed lookup, and it turns an unbounded duplicate risk into a bounded window of milliseconds. That window is not zero: two workers can both read “not present” and both write. Where that matters, the constraint has to move into the database — a unique index on the pair of fields that identify the action — so the second write fails loudly instead of quietly succeeding.

That failing loudly part is the point: a duplicate that raises an error is a bug you can see. A duplicate that succeeds is a duplicate you find out about from a customer.

Reconciliation is not optional

Some duplicates still get through, and some actions get attempted by a human and an agent in the same hour. The only reliable answer is a periodic reconciliation job that compares what the agent believes it did against what the downstream systems actually contain.

It is cheap to build, it is the difference between a system that self-corrects and one that accumulates quiet damage, and it produces the number the business actually wants: how many actions per thousand were duplicated or reversed. That belongs next to the four numbers you already track, because a rising duplicate rate is often the first sign that an integration has started timing out under load.

Three questions before the agent gets write access

Before a workflow is allowed to act rather than advise, the team should be able to answer all three in one sentence each.

What is the intent key, and where is it generated? If the answer involves the model’s own reasoning, it is not a key.

What happens if this action runs twice — does it duplicate, overwrite, or fail? For each write endpoint, someone should know which is true. “I think it is fine” is not one of the three.

Who notices the duplicate, and when? If the answer is the customer, the reconciliation job does not exist yet.

None of this involves the model. That is the uncomfortable part of agentic systems: the intelligence is the component you buy, and the safety is the component you build around it. The teams that ship durable agents are the ones who assumed, from the first design session, that every action would eventually be attempted twice — and made that boring. If that framing sounds like the missing piece of your own rollout, it is the kind of thing we do on our services page.

Want this running in your business?

Start with an automation audit — we map the highest-value workflows and show you what an agent can take on.

Book an Audit