agent runtime lab en · 2026 · Technical Research Note
10. Preventing Duplicate Side Effects
Retrying a model call spends computation; retrying a send, payment, or create operation can change the external world twice
Using idempotency keys, request fingerprints, external status queries, and recovery states for the crash window after an action.
Research edition · 8 chapters · 4 minute read · Updated 2026-07-27
An Agent asks a tool to send email. The service accepts the request, but the local process exits before recording the result. On recovery, the trace contains “started” but not “completed.”
Repeating the call can send two messages.
What idempotency provides
An idempotent operation has the same externally observable result whether applied once or repeatedly. Reading a file is close to naturally idempotent. Sending a message, creating an order, and appending a record generally are not.
A runtime can attach an idempotency key to a side-effecting call. A service receiving the same key returns the first outcome instead of executing again.
| Field | Purpose |
|---|---|
operation_id | Runtime identity for the action |
idempotency_key | External deduplication identity |
request_fingerprint | Proves arguments did not change |
external_id | Resource identity returned by the service |
status | Started, completed, or unknown |
The key is bound to normalized arguments. Reusing it after changing parameters must not return an unrelated old result.
Three tool classes recover differently
| Tool class | Example | Recovery |
|---|---|---|
| Read-only | Read a file | Retry |
| Queryable side effect | Create a ticket | Query by key or external identifier |
| Unqueryable side effect | Send without receipt | Mark unknown and request human decision |
One call protocol does not imply one retry policy.
Persist intent before execution
Before the external call, persist the normalized argument summary, permission decision, and idempotency key. After the call, save the external identifier and result. This order narrows the window in which the action happened without any local record.
It cannot remove all uncertainty. A timeout can occur after service completion but before the response arrives. Server-side idempotency or a status endpoint remains necessary.
The model does not decide retry safety
The model may propose a retry from an error message. The runtime decides from deterministic metadata: side-effect class, idempotency support, query support, retry count, and permission validity.
A fingerprint prevents incorrect reuse
The key identifies the logical action. The fingerprint identifies its normalized parameters. A repeated key with the same fingerprint returns the original outcome. A repeated key with a different fingerprint produces a conflict.
Canonicalization fixes field order, path representation, and default values before hashing. Otherwise equivalent requests diverge or different requests collapse.
Deduplication records have a lifetime
Records cannot be stored forever, but they cannot expire while recovery remains possible. Retention covers the maximum client retry window, queue delay, and manual recovery period. Tool contracts define whether a reused key after expiration starts a new action or is rejected.
Fault injection
Terminate the process after external success and before tool.completed. On recovery verify:
- The same idempotency key is reused.
- External status is queried first.
- The same approved action does not request approval again unnecessarily.
- Changed arguments create a new operation or a conflict.
- An unconfirmed outcome becomes
unknown, not an automatic retry.
Also submit the same key concurrently and verify that only one executor performs the action. Check how failed outcomes may be retried and whether the status API returns the original external identifier.
“Retry at most three times” limits duplication; it does not prevent it. Reliable recovery combines action semantics, persistence order, and external API support.
The key identifies one logical intent
An idempotency key and request fingerprint form one operation. Reusing a key with a different recipient or body is rejected.
Records distinguish reserved, executing, succeeded, failed_retryable, and failed_final. A duplicate either reuses a result, waits for the current executor, or follows a proven retry path.
A local transaction cannot atomically include a remote email or payment. The process can stop after remote acceptance and before local commit. Recovery passes the key downstream, queries business state, or performs an explicit compensating action.
Fault injection stops before and after intent persistence, remote acceptance, and result commit. Many attempts may appear in the trace, but the external system contains one logical object and the runtime accepts one result.
Statement: If no specific statement in the content, the copyright belongs to sshipanoo . Reprint please indicate the link of this article.
(The content is authorized with CC BY-NC-SA 4.0 protocol)
Title:10. Preventing Duplicate Side Effects
Link:https://www.sshipanoo.com/en/blog/ai/agent-runtime-lab/preventing-duplicate-agent-side-effects/
