Bible Network Crypto DeFi Onchain RWA AI Agent Stablecoin Chain SAFU CryptoTax DeFAI AGI Claude Me Claude Skill Claude Design Claude Cowork
Independent Media
Not affiliated with any project
Deconstructing Autonomous Agents in Crypto
aiagent-bible.com
LATEST
LangGraph Deep Dive: Building a Cyclical DeFi Agent Step by Step, and Three Pitfalls Along the Way  ·  How to Test an AI Agent Before Production: A Practical Testing Framework, and Why Traditional Unit Tests Aren't Enough  ·  Why Your Agent's Output Looks Right But Isn't: The Reliability Gap Between Format and Truth  ·  How Beginners Should Choose Their First Agent Framework: Stop Asking 'Which Is Strongest,' Ask 'Which Gets Me Running Today'  ·  Prompt Injection Defense for Onchain Agents: Why 'Tell the Model Not to Trust External Instructions' Is the Least Useful Defense, and the Layered Architecture That Actually Works  ·  Agent Retry and Error Handling Strategy Design: Why 'Just Retry on Failure' Is the Default Logic Most Likely to Bankrupt You
Glossary · Tool Use & MCP

Idempotency

Tool Use & MCP Intermediate

30-Second Version · For the impatient
Idempotency means an operation produces exactly the same end result whether it's executed once or repeated multiple times — this property lets an Agent's retry logic safely call the same operation again without worrying that repeated execution causes unintended duplicate side effects (a duplicate charge, a duplicate order).
Full Explanation +
01 · What is this?

If a tool function itself isn't idempotent (e.g., calling an external API to place an order), how can an Agent make its own retry logic still safe without modifying that external API?

If the external API itself doesn't provide idempotency-key support (a third-party service's order-placement endpoint is designed as 'every call produces a new order,' without letting you pass an identifier to avoid duplicates), you can't change the other party's API behavior, but you can add an extra protection layer at your own Agent's architecture level — two common approaches.

Method one: deduplicate requests in your own system before calling the external API. Concretely, every time the Agent prepares to initiate an order operation, it first records 'a unique identifier for this operation' and 'a hash of the operation's content' in its own database; before actually calling the external API, it checks whether there's already a record with completely identical content already processed recently — if so, this might be a duplicate trigger (like the earlier retry-strategy article's mention of some step misjudging and causing the same task to trigger twice), and this call is skipped, no duplicate order placed; if not, the external API is actually called, and this call is logged. The essence of this method is adding a self-controlled idempotency layer in front of a 'non-idempotent external API,' transferring the responsibility for idempotency from the external API to your own system.

Method two: restrict retry timing and scope, only retrying when you can confirm 'the previous call definitely failed.' Most situations causing an Agent to accidentally retry a non-idempotent operation stem from 'not being sure whether the previous call succeeded or not' (a network timeout — you don't know if the request never arrived, or arrived but the response never came back). Only retrying when you can clearly judge 'the previous call definitely failed, the other system did nothing at all' (receiving a clear error response code, rather than an ambiguous timeout scenario) can substantially lower the probability of accidentally triggering a duplicate order — but this method isn't foolproof, since an ambiguous scenario like network timeout inherently can't be 100% ruled out, which is why method one (a self-controlled idempotency layer) is usually more reliable and should be prioritized.

Practical recommendation: if the operation involves actual money or irreversible consequences, prioritize method one (building your own idempotency layer), even requiring extra engineering investment; method two can layer on as a supplementary measure, but shouldn't be the only line of defense, since network environment uncertainty inherently can't be fully eliminated.

02 · Why does it exist?

When designing an idempotency key, should this identifier be generated by the Agent, or by the user initiating the request? What's the difference between the two approaches?

Each approach fits a different scenario, and the core difference is 'how broad the deduplication scope should be for a logically single operation.'

The Agent generating its own idempotency key: fits the scenario of 'the Agent autonomously decides to retry' — e.g., the Agent judges the previous call failed due to a network timeout and decides to retry; at this point the Agent should carry forward the identifier already generated for 'this same operation' originally, rather than generating a new identifier on every retry (if every retry used a new identifier, the idempotency-key mechanism would completely lose meaning, since the backend would treat every retry as a brand-new, never-processed operation). In this scenario, the identifier is generated 'the first time this operation is proposed,' and no matter how many retries follow, the same identifier is carried forward.

The user (or the user-side application) generating the idempotency key: fits the scenario of 'the same user intent possibly triggered multiple times through different channels' — a user clicks an 'execute' button in an application interface, and due to network delay, unsure whether it was successfully submitted, clicks it again. In this case, if the idempotency key is generated on the user side (the application interface) and attached to this instance of 'user intent,' even if these two clicks trigger two independent Agent calls behind the scenes, as long as both calls carry the same user-side-generated identifier, the backend can still correctly judge this is the same intent and execute only once.

The essential difference between the two: an Agent-generated idempotency key protects against duplication at the 'Agent's internal retry logic' layer; a user-generated idempotency key protects against duplication at the broader, higher-level 'user intent' layer, covering both the user's own accidental duplicate operation and the Agent's internal retries (since a user-side-generated identifier theoretically carries through the entire processing chain, no matter how many times the Agent internally retries).

Practical recommendation: if the system architecture has user intent triggered through an explicit interface action (clicking a button), prioritize generating the identifier on the user side, so it simultaneously covers both 'user's duplicate operation' and 'Agent's internal retry' as duplication sources, giving more complete protection scope; if the entire flow is triggered entirely autonomously by the Agent, with no user real-time operation step (like the earlier automated yield-optimization Agent, whose task is triggered by the Agent's own judgment), then the Agent generating its own identifier at the moment the task is created is sufficient, no need for extra user-side identifier-generation logic.

03 · How does it affect your decisions?

If an Agent system restarts or crashes, and previously recorded idempotency-key information wasn't persisted, does the protection disappear after restart? How should this be designed to avoid this gap?

This is a genuinely real risk. If idempotency-key records only exist in memory (just a runtime variable, not written to a database or other persistent storage), once the Agent system restarts or crashes, all these records are lost entirely — after restart, if a duplicate request arrives that was 'actually already processed before the crash,' the system would misjudge it as a brand-new request and execute it again, exactly the scenario idempotency keys are meant to prevent — if the idempotency-key record itself isn't reliable enough, the protection mechanism is rendered meaningless.

The correct design principle: the idempotency-key record itself must be written to persistent storage, not just kept in memory. Concretely, before every operation is prepared for execution, first write 'this operation's identifier' and 'the operation content about to be executed' to the database synchronously (not waiting until the operation completes to record it — this is critical, and the reason is explained below); once the operation completes, update the 'execution result' into this same record. This way, even if the system suddenly crashes mid-operation, upon restart it can at least query 'this identifier's operation had already begun executing before, but it's uncertain whether it completed' — rather than having no record at all.

Why write the record 'before execution' rather than 'after execution': if you choose to write the idempotency-key record only after the operation completes, there's a time-window risk — if the Agent crashes between 'the operation has actually finished executing' and 'the record of this execution gets written to the database,' upon restart, the database has absolutely no record of this operation (since the record hadn't yet been written), and the Agent would misjudge 'this operation hasn't been executed yet,' executing it again, causing the earlier-mentioned duplicate-charge type of problem. Conversely, writing a 'about to execute' record first, then actually executing the operation, means even if it crashes midway, the database at least has a record that 'this operation was once triggered' — upon restart, it can first verify 'has this operation actually already succeeded in the external system (on-chain or a third-party API)' before deciding the next step, rather than executing it again with no clue at all.

An additional practical consideration: idempotency-key records can't be retained indefinitely (or storage space would keep growing) — usually a reasonable retention period needs designing (24 hours or 7 days, depending on how long a reasonable retry window is for the business logic), and old records beyond this period can be cleaned up, since beyond that time, the probability of a duplicate request is extremely low, and the marginal value of continued retention is low.

04 · What should you do?

In a multi-agent system, if Sub-agent A already successfully executed an operation using an idempotency key, but due to information loss during Handoff, Sub-agent B initiates another request for the same logical operation, can the idempotency-key mechanism still protect against this cross-Agent duplication?

Yes, it can protect against it, but on the premise that 'the idempotency key itself is handed off along with the task,' rather than existing only within Sub-agent A internally — echoing the principle repeatedly emphasized in the earlier Handoff entry that 'the handoff package must include sufficient metadata.'

The key design for cross-Agent idempotency protection: an idempotency key shouldn't be treated as Sub-agent A's internal implementation detail — it should be treated as a property of 'this logical operation' itself, assigned from the very start of the task, and fully passed along to Sub-agent B through the Handoff package. Concretely, if the task itself already generated a unique task identifier at the very beginning (when the Orchestrator dispatches it), this identifier should carry through the task's entire lifecycle — no matter how many Handoffs occur along the way or how many different Sub-agents process it, the same logical operation should carry forward the same identifier.

What happens if the Handoff process genuinely loses this identifier: as in the scenario you described, Sub-agent B, not having received the original identifier, can only generate a new identifier itself and initiate the request. From the backend's perspective (the system actually responsible for executing the transfer), this is a request carrying a brand-new identifier — the idempotency-key mechanism would judge it as a 'never-before-processed new operation' and execute it normally, causing a duplicate-execution problem. This means the idempotency-key mechanism itself hasn't failed — what failed is 'the completeness of the identifier's transmission during Handoff.' The problem is upstream (Handoff information loss), not downstream (the idempotency-key mechanism itself).

How to guard against this scenario: beyond content like 'handoff package completeness testing' that the testing framework mentioned earlier should cover, an additional protection layer independent of the Handoff mechanism can be added — before actually executing an operation, beyond checking 'has this identifier been processed,' also additionally check 'is this operation's specific content (target address, amount for a transfer) highly similar to some operation already processed recently.' Even with a different identifier, if the operation content is nearly identical and the timing close, it should trigger an additional confirmation flow (human intervention, or at minimum a logged alert), rather than accepting it wholesale and executing directly. This content-level similarity check can serve as a supplementary defense line to the identifier mechanism, still providing a layer of protection when the identifier itself fails for some reason (Handoff information loss).

Core principle: idempotency protection in a multi-agent system can't be designed within a single Sub-agent alone — it must be treated as a consistency requirement spanning the entire task lifecycle, across all Handoff steps. This is the inevitable result of extending the earlier Handoff entry's 'handoff package completeness' principle to this specific idempotency scenario — however rigorously designed a mechanism is at a single step, if it doesn't account for information-transmission completeness during multi-agent collaboration, it can still fail in the gaps between collaboration steps.

Real-World Example +

An idempotency implementation case for an Agent wallet's transfer operation

An Agent responsible for automatically executing on-chain transfers for users designed idempotency as follows: identifier generation timing — when the Orchestrator dispatches the task, a unique task_id (combining a timestamp and a random string) is immediately generated; this task_id serves as the idempotency key, written to the database with status marked pending, this action happening before the on-chain transfer operation is actually called. Pre-execution check: before the Agent prepares to initiate a transfer, it queries the database for whether a record with the same task_id and a status other than pending already exists — if status is already completed, meaning this transfer already succeeded, it directly returns the original execution result without re-sending the transaction; if status is failed_confirmed (meaning the previous attempt was already confirmed as failed, with no trace whatsoever on-chain), only then does it genuinely re-initiate the transaction. Handling ambiguous in-progress state: if the queried status is pending (meaning the previous attempt was cut off midway, uncertain whether it actually occurred on-chain), the Agent doesn't directly assume 'not executed yet' and re-send the transaction — instead, it first queries on-chain, using the previously recorded transaction hash (if one was logged) to confirm whether this transaction already exists on-chain, only re-initiating once confirming this transaction genuinely doesn't exist on-chain, avoiding a duplicate transfer caused by misjudging an ambiguous state. Cross-Sub-agent transmission: if this transfer task involves multiple Sub-agents collaborating (one judging whether to transfer, another executing it), task_id serves as a required field in the Handoff package, explicitly passed to the execution Sub-agent; upon receiving the task, the execution Sub-agent's first step is using this task_id to check the earlier-mentioned pre-execution state, rather than generating a new identifier itself. A problem found in actual operation: during testing, the engineering team simulated a scenario of 'the system crashing and restarting after the transaction was sent but before receiving on-chain confirmation.' They found that if only checking 'does the database have this record' plainly, the system after restart would get stuck not knowing what to do because the record shows pending (an uncertain state). After adding the step of 'actively querying on-chain status to clarify the ambiguous situation,' the system could correctly judge, in this crash-restart scenario, whether to re-initiate the transaction or directly report it had already succeeded, avoiding a predicament only resolvable through human intervention.

Diagram
Idempotent vs Non-Idempotent: What Retry Does to Each雙欄對比圖:左欄呈現冪等操作(查詢餘額)重試三次,結果始終一致;右欄呈現非冪等操作(轉帳)如果沒有冪等鍵保護,重試三次會疊加成三倍效果,中間用冪等鍵機制的加入,展示如何把右欄的風險行為,轉換成左欄的安全行為。Idempotent vs Non-Idempotent Under RetryIdempotent: Query BalanceAttempt 1Attempt 2Attempt 3Result: balance = $500 (always)Non-Idempotent: Transfer $100Attempt 1Attempt 2Attempt 3Without protection: $300 sent, not $100Fix: Idempotency KeyEach retry attempt carries the SAME unique keyBackend checks: "has this key been processed?"If yes → return original result, don't re-executeResult: $100 sent, no matter how many retriesAI Agent Bible · aiagent-bible.com
Feel free to share. Please credit the source.
Common Misconceptions +
✕ Misconception 1
× Misconception 1: as long as an Agent's retry logic has a count cap set, that's already safe enough, and idempotency doesn't need extra handling. A retry-count cap only solves the 'infinite retry' problem — it does nothing for the 'retry causing duplicate side effects' problem. Even retrying just once, if the operation itself isn't idempotent, still causes real damage like a duplicate charge or duplicate order — the two are separate problems that must be handled independently.
✕ Misconception 2
× Misconception 2: an idempotency key only needs to be cached in memory, no need to write it to a database or other persistent storage, since an Agent won't restart frequently in most cases. Even if restart frequency is low, once a restart or crash genuinely occurs, the idempotency-key record in memory is entirely lost, rendering the protection mechanism meaningless at that exact moment — considering idempotency design exists precisely to guard against 'infrequent but severe-consequence' situations, using 'restarts don't happen often' as a reason to skip persistence directly contradicts the design's original purpose.
The Missing Link +
Direct Impact

Idempotency design's core tradeoff is protection rigor versus extra engineering and storage cost. The more detailed the idempotency-key record (a full snapshot of operation content, an additional content-level similarity check), the broader the range of duplication scenarios it can guard against, but the more database writes, queries, and storage space required, adding a not-insignificant extra burden for a high-frequency-operation system. Another tradeoff is identifier-generation layer (Agent-internal versus user-side): a user-side-generated identifier has broader protection scope (covering both user's duplicate operation and Agent's internal retry), but requires extra identifier-generation logic design on the user side, adding front-end/back-end coordination complexity; a purely Agent-internally-generated identifier is simpler to implement, but has narrower protection scope, unable to cover user-side duplicate triggering. Recommendation: for core flows involving actual money or irreversible operations, even at extra engineering cost, adopt the most rigorous persistent idempotency-key design, and where feasible, have the identifier start generating from the user side, carrying through the entire processing chain; for non-core, low-risk operations (pure information queries or analysis), idempotency design can be omitted or a simplified version adopted, saving engineering resources for the segments that genuinely need rigorous protection.

Ask a Question
Please enter at least 10 characters