The tool call timed out. Did the action happen?

In an early-July discussion about agentic systems in banking, a commenter raised a problem that could get lost in the argument over LangChain and LangGraph: a tool call times out, the agent retries, and the first attempt may already have changed the remote system. Other replies favored building the orchestration directly or making execution explicit as a state machine. Neither choice, by itself, answers whether the action happened.

Consider a hypothetical support agent that issues a replacement order for a damaged item. The order service accepts the request and creates the shipment, but the response never reaches the agent. After a timeout, the agent sees a failed tool call and tries again. The customer now has two replacements on the way.

The model may have understood the customer’s request perfectly. The failure comes from treating a missing response as evidence that the action did not happen. The thread identifies a useful design question, although it does not document a particular duplicate-write incident: what evidence makes the next attempt safe?

Preserve the identity of the action

An idempotent operation can be repeated without adding another effect. AWS’s Builders’ Library account of safe retries describes a caller-supplied request identifier that lets the service recognize a repeated request and return the existing operation’s result. Recording that identifier must be coordinated with the mutation. Otherwise, a failure could leave a completed action that the service cannot recognize when the caller tries again.

For the replacement-order example, the application could create an operation record tied to the approved replacement for that support case. It would persist the operation’s identifier before dispatch and send it with every attempt. The order service would associate the shipment with that identifier. A retry could then recover the existing shipment’s result.

The identifier belongs to the business action. A fresh identifier for every network attempt would allow a second shipment, and asking the model to invent one would make continuity depend on its next response. The harness, the software that dispatches and records the agent’s actions, should recover the pending operation from durable state when execution resumes.

Matching arguments alone is insufficient. Two requests for the same item and address might be a retry, or they might be two legitimate replacements. The application needs an explicit rule for which approved request an invocation fulfills. Otherwise, a model that proposes the same action again in a later turn can bypass duplicate detection simply by causing a new operation to be created.

A checkpoint does not settle the remote outcome

On July 14, Robert Ross published The Agentic Loop: Three loops in a trench coat. He separates inference, tool execution, and human approval, then points out that a process waiting hours for approval must survive restarts. He identifies durable execution frameworks such as Temporal as a way to handle that wait. His examples are explicitly illustrative pseudocode.

That account makes the handoff from a proposed tool call to execution visible. The retry problem raised in the banking thread begins at the next handoff: the external service has acted, but the result has not reached the saved conversation.

A saved conversation can show that the agent intended to place an order. It cannot establish that the order service committed the write. There is an interval between the remote action and the local record of its success, and a process can fail during that interval.

Temporal’s documentation on activity idempotency describes this case explicitly: a worker can finish an action and crash before reporting completion. The activity may then be retried. The documentation places enforcement of an idempotency key in the service being called. Durable orchestration helps resume the work, while the receiving service must make repeated execution safe.

In the replacement example, recovery should retain the pending operation’s identity and its unresolved outcome. Depending on the order service’s contract, the application can query the operation’s status or repeat the request with the same identifier. A local log entry saying that the request was sent supplies useful evidence for investigation, but does not establish whether a shipment exists.

Ross also discusses returning tool errors to the model. For the timeout case, the content of that result matters: “failed” invites another attempt without explaining the uncertainty. A result describing an unresolved operation gives the agent a reason to wait for recovery or tell the user that confirmation is pending. The application must enforce that restriction even if the model asks to place another order.

Check the limits of the retry contract

An idempotency key is useful only within the receiving service’s documented rules. Stripe’s API documentation, for example, says it stores the first request’s status and body, including a server-error response. It also describes parameter checks and key pruning after a retention period. Repeating a key therefore does not necessarily turn an error into success, and a key that has been pruned may no longer prevent another execution.

For an order service, the design review needs to establish how long duplicate protection lasts and whether recovery can outlive it. A support case might remain suspended for days. If its operation identifier expires sooner, resuming the case requires checking the authoritative order record before attempting the write again. Changing the item or address also needs explicit handling; the system must not silently treat an altered replacement as a retry of the approved one.

Ross’s approval step lets a person decide whether an action should proceed. After an ambiguous timeout, that person also needs to know whether it already proceeded. A legacy ordering endpoint may offer neither duplicate suppression nor an authoritative way to inspect an operation. After a timeout, the application may have no safe next attempt. Automatic retries should stop there, with the unresolved action sent to someone who can investigate the order records. That person needs additional evidence before deciding what to do; a dialog asking them to click “retry” leaves them with the same uncertainty as the agent.

Test the failure after the write

The useful test is to let the order service create a replacement and then discard its response. Restart the agent process before it records success. On recovery, the system should resolve the original operation and report the existing shipment without creating another one.

Repeat the test with concurrent attempts and with a model response that asks to place the order again. Check the order service’s records, as well as the execution trace: one shipment should correspond to the approved replacement, and every attempt should be attributable to that operation. Test expired identifiers separately, because recovery outside the service’s retention period needs its own decision.

This gives the July framework debate a concrete test. Whether the team chooses a graph engine or writes its own loop, it should demonstrate recovery after a successful write whose response was lost. An inspectable graph can help explain the attempt; the receiving service’s records must establish that the approved replacement produced one shipment. That is the evidence I would ask for before accepting a claim that the tool loop is ready for production.

← Field notes