The Double-Send Vulnerability in Agent Restarts
A seemingly straightforward agent restart can introduce a critical flaw: the risk of sending the same message twice. This vulnerability arises when an agent’s logic conflates the completion of a task (execution recovery) with the successful delivery of its output to an external system (outbound-delivery recovery). When these two distinct concepts are treated as a single boolean state, a restarted agent might re-process and re-send a message that was already sent.
Consider an agent designed to process a user request, call a tool, and then notify the user of the result. The agent successfully calls the tool, but before it can confirm the outbound notification was sent and acknowledged by the delivery system, it crashes or is restarted. The agent’s state might indicate that the “work” is done, but the delivery mechanism still has a pending outbound item. Upon restart, the agent reconstructs its state, sees the “work done” flag, and then attempts to send the notification again. Simultaneously, the original, pending outbound item might still be in the delivery queue, leading to a duplicate message being dispatched.
This issue is not hypothetical; it affects agents that rely on shared notions of completion for both internal processing and external communication. The core problem lies in treating execution recovery and outbound-delivery recovery as interchangeable. Execution recovery is about whether the agent successfully completed its internal unit of work – did it call the tool, perform the calculation, or retrieve the data? Outbound-delivery recovery, on the other hand, is about whether the external system, such as a webhook endpoint, an email server, or a chat platform, has successfully accepted the notification. These are fundamentally different concerns with different failure modes and boundaries.
An agent might complete its internal task and crash before it even logs that the task is complete. Similarly, a dedicated delivery worker could successfully submit a message to an external API but crash before receiving the final acknowledgment from that API. If both scenarios are collapsed into a single “done” state, the system becomes brittle. A restart might then trigger a resend of already delivered information, or worse, a missed notification if the delivery system failed after the agent thought it was done.
Separating State Machines for Robustness
The robust solution involves modeling execution recovery and outbound-delivery recovery as two distinct state machines. This separation ensures that the completion of one process does not erroneously imply the completion of the other. Each state machine should track its own progress and outcomes independently.
For execution recovery, the state machine would track steps like: receiving request, preparing tool call, executing tool call, and recording tool call result. A “completed” state here means the agent has finished its internal computational or logical steps. It does not imply anything about whether the result has been communicated externally.
The outbound-delivery state machine would track steps such as: preparing message for delivery, sending message to delivery service, receiving acknowledgment from delivery service, and marking message as delivered. A “completed” state here means the external system has confirmed receipt of the message. This state is independent of whether the agent itself is still running or has crashed and restarted.
By maintaining these separate state machines, an agent restart can accurately reflect its true status. If the execution state machine shows completion but the delivery state machine shows a pending item, the system knows to only process the pending delivery. If both are marked as complete, no further action is needed. Conversely, if execution is complete but delivery is not, the agent can safely retry the delivery without re-executing the original task. This granularity prevents duplicate messages and ensures that no notifications are lost due to transient failures or restarts.
The implications of this design pattern extend beyond simple message delivery. Any agent or system that performs a core computation and then communicates the result externally faces this challenge. This includes reporting systems, notification services, and even some forms of data synchronization. Implementing distinct state machines for internal processing and external communication is key to building resilient and reliable agentic systems that can withstand restarts without introducing data duplication or loss.
The surprising detail here is not the complexity of agent restarts, but how a simple conflation of two distinct recovery states—execution and delivery—can lead to a fundamental flaw like double-sending. Developers often focus on the agent's internal logic, overlooking the critical handoff to external systems and their own recovery mechanisms.
If you manage an agent-based system that communicates externally, you must audit its recovery logic. Specifically, check if execution completion and external delivery confirmation are treated as a single state. If they are, your system is susceptible to duplicate messages upon restart. The fix requires refactoring the recovery logic to maintain separate state machines for internal task completion and external message delivery confirmation. This is not a trivial change, but it is essential for ensuring message integrity and preventing customer-facing errors.
The path forward involves a disciplined approach to state management. Each component of an agent’s workflow—from computation to communication—must have its own well-defined state and recovery process. Treating these as atomic, independent units is the only way to build truly robust and dependable AI agents.
