Share
Duplicate Writes: Idempotency Testing for AI Agents
October 15, 2026Read time: 6 min

Duplicate Writes: Idempotency Testing for AI Agents

A 502 after a committed write, a retry, and the customer has two orders. Why AI agents create duplicate writes, and how to test idempotency before release.

In short: an AI agent creates duplicate writes when a write commits but the response is lost and the agent, its SDK or the model retries. Prevent it with an idempotency key for the business action, generated in tool code, and prove it with a test that commits a write, fails the response with a 502 or timeout and checks that exactly one record exists.

Why do AI agents create duplicates?

A normal service has one retry layer. An agent has three: the HTTP client, the SDK and the model itself. The model is the dangerous one. When it sees "timeout" or "502", it often decides to call the tool again with slightly different arguments. The SDK sees a new request and generates a new key. The target system sees a new order.

The typical sequence:

  1. The agent calls create_order.
  2. The ERP commits the order.
  3. A gateway times out or returns a 502 before the response arrives.
  4. The model reads that as "failed" and calls create_order again.
  5. The customer now has two orders, or two charges.

In procurement this means two purchase orders at the supplier, and in finance two refunds. The domain cases are in testing AI procurement agents and testing AI finance agents.

HTTP itself warns about this: a client shouldn't automatically retry a non-idempotent request unless it knows the request is idempotent (RFC 9110, section 9.2.2).

How idempotency keys work

APIs make writes retry-safe with a key supplied by the caller:

For agents, the key must represent the business action, not the HTTP call.

Where should the key come from?

  • Good: derived from the task, for example request R-1042 / create_purchase_order / supplier S-17. A model retry of the same action produces the same key.
  • Risky: a random UUID per tool call. It only protects against transport retries.
  • Wrong: no key, trusting the model to remember it already wrote.

Generate the key in tool code, never in the prompt. If the target system has no idempotency support, have the tool look for an existing record with the same business reference before writing, and protect that check against concurrent runs.

How to test for duplicate writes

Duplicates never appear in a quiet test environment, because nothing fails at the wrong moment. Put a fault-injecting proxy between the agent and a sandbox of each target system, then run each critical write flow through these cases:

ScenarioHow to inject itExpected end state
Response lost after commitCommit, then return 502Exactly one record
Timeout after commitDelay the response past the client timeoutExactly one record
Rate limitReturn 429 on the first attemptOne record after backoff
User repeats the request"Did it work? Do it again."The agent checks first; one record
Restart mid-taskKill and resume the agent after the writeOne record
Double triggerFire the same request twice in parallelOne record

What to assert:

  • Count by business key. Exactly one record carries the request's reference, or zero for a clean failure.
  • Totals reconcile. For billing, the ledger shows one charge.
  • Report matches state. "Failed" with a record present is also a bug, because a person will redo the action by hand. That overlaps with hallucinated success.
  • Attempts are bounded. If a dependency keeps failing, the agent stops after a set number of retries and reports a clean failure instead of looping.

Why test this before every release?

A correct idempotency design can still regress. A prompt edit makes the model retry with different parameters, so the key changes. A new tool skips the shared write wrapper. A model upgrade starts reading "timeout" as "failed, try again". None of this breaks a unit test, because mocked suites never return a 502. All of it shows up when the fault-injection cases run against a real sandbox before the release, which is the job of a release gate. The full method is in how to test AI agents that write to your CRM or ERP. Field-level problems such as truncated IDs and unit mix-ups are covered in AI agent wrote wrong data to your CRM or ERP, and the AI agent production readiness checklist puts the retry check next to the others to run before launch.

FAQ

How do I stop an AI agent from creating duplicate records on retry?

Attach an idempotency key derived from the business action to every write, generate it in tool code, and test it by committing a write and then failing the response with a 502 or timeout. The system must end with exactly one record.

Why don't my SDK's idempotency keys prevent duplicates?

SDK keys cover the SDK's own retries. When the model calls the tool again, the SDK sees a new request and makes a new key. The key has to come from the business intent.

What is a 502 retry loop?

An agent retrying a write again and again because a gateway keeps returning 502s. If the backend committed some of those writes, each retry can add a record. Bounded retries plus idempotency keys prevent it, and fault-injection tests prove it.


Shipping AI agents with write access to ERP, CRM or billing? Agentiqa sets up high-confidence release gates for vertical AI agents in high-liability domains, including injected 502s and timeouts that must end in exactly one write or a clean failure. The gates run before every release, and the first release gates are live within 7 minutes. Book a 20-Minute Mutation Risk Audit.

Testing your own web app? Start free with the Agentiqa Community Edition.