Sulayman Bowles
Back to writing

Software systems

When an AI tool call times out, did the action happen?

A lost response leaves an operation uncertain. Durable identity, atomic receipts and a current-state check give the application a defensible recovery path.

Monochrome generative point study “Lantern Bloom,” a mathematical form rendered in black and white.

A timeout means the caller stopped waiting. The database may already have committed. I would recover a state-changing tool call by querying the original operation ID before another write, because a fresh identifier can turn a lost response into a duplicate action.

In the illustrative bookmark app, an assistant marks a link reviewed, the database commits, and the connection breaks before the response arrives. The person then edits the bookmark again. Recovery must identify the first action while preserving that newer decision.

RFC 9110 recommends that clients avoid automatically retrying a non-idempotent request unless they know its semantics are idempotent or the first request was never applied. A missing response supplies neither fact.

Method & limits

Primary documentation checked October 26, 2026. The bookmark scenario and pseudocode are authored design illustrations. No deployed implementation or reliability benchmark is reported.

The business operation needs a durable identity

AI orchestration adds another layer around this familiar problem. In OpenAI’s function-calling flow, application code executes the function and returns its output to the model. The conversation and the application’s database are separate systems. The protocol’s call_id associates an output with a call; the application still needs a specified identity for the underlying business operation across attempts. Function-calling flow, result correlation

Create the identity before the first attempt and keep it through recovery. Bind it to the authenticated account and validated action, target, expected revision, and arguments. Derive account scope from server authentication, then reject the same ID with changed intent. AWS describes this mismatch check: identical arguments alone can’t identify intent, because a person may deliberately request the same action twice.

The server must also enforce the identity when requests arrive together. Two clients can both observe that an operation is absent before either writes it. Put a uniqueness rule over non-null account and operation identifiers in the database, and define how a competing request waits, conflicts or recovers the committed result. PostgreSQL documents uniqueness across a combination of columns; an application-side existence check does not provide that constraint. Unique constraints

The effect and receipt share one transaction

For the bookmark example, commit the mutation and a minimal operation receipt in one database transaction. Recording success before changing the bookmark can produce a receipt for an action that never happened. Recording it afterward in a separate transaction can leave a completed action without its recovery evidence. A shared transaction makes those local writes succeed or fail together. PostgreSQL’s transaction documentation describes this atomic boundary. Transactions

The pseudocode covers first execution and replay within a valid recovery context, created before the first attempt. Reject an expired context before reclaiming an identifier. The unique-operation claim must handle concurrent attempts, and revision comparison belongs in the database update. Only a newly claimed operation may mutate; a pending one waits or conflicts. Concrete SQL needs conflict and transaction-retry handling.

transaction:
  authorize_current_caller(
    bookmark
  )
  require_valid_context()
  operation = claim_unique(
    account,
    operation_id,
    validated_intent
  )
  require_same_intent(
    operation, validated_intent
  )
  if operation.already_committed:
    return operation.receipt
  if not operation.newly_claimed:
    abort_with_pending
  revision = conditional_review(
    bookmark, expected_revision
  )
  if no revision matched:
    abort_with_conflict
  save_receipt(
    operation_id,
    intent_digest,
    revision
  )
commit

Keep the receipt small: the operation identity, a binding to its intent, the committed outcome and the resulting revision may be enough. It need not contain a copy of the bookmark or its private text. Make the status interface and retention window explicit, so a caller knows what evidence remains available after a lost connection or a restarted process.

Recovery queries the original operation

Query the same operation ID. A committed receipt confirms the earlier action; a running operation calls for observation. A confirmed rejection before execution may permit retry under the contract. An ambiguous or unavailable result stays unknown. Give those states machine-readable outcomes so the model and interface can choose the next step.

Expiration is particularly easy to misread. AWS describes an EC2 policy that retains a request identifier for the resource’s lifetime plus an interval for late requests. That is a service-specific choice, and a reminder to read the actual retention contract before relying on an old identifier. Late requests and retention

If your own service can no longer distinguish an unseen operation from a previously applied operation whose receipt expired, absence is not permission to execute again. Report the uncertainty. Inspect current state and any other authoritative evidence. For an irreversible action, unresolved ambiguity may require human reconciliation. A newly issued identifier must not quietly turn an uncertain retry into a new authorized action.

A worker can finish its write before the caller receives acknowledgment. The crawl frontier’s commit boundary shows how scheduling handles that gap.

Revision 13 doesn’t overwrite revision 14

Suppose the first action committed revision 13, then the person made a new review decision at revision 14. The receipt proves what happened at revision 13; revision 14 remains the current decision. Conditional writes protect newer work, while recovery reports the earlier outcome as history. HTTP’s If-Match precondition serves a related purpose for representations with suitable validators. HTTP preconditions

After recovery, fetch a fresh projection through the application’s current authorization rules. Access may have been revoked, the record may have been deleted, or the returned fields may have changed. Knowing an operation identifier should not grant access to the receipt or resource. Return only what the present caller can inspect, and keep the earlier operation outcome separate from that current view. Recovery reads must not reapply an earlier review over a newer decision.

External effects need their own recovery contract

The transaction above commits writes inside one database. Email, payments, and other external effects need the receiving service’s documented recovery contract and a way to reconcile partial progress. A local receipt describes the local commit; certainty about an external action requires that service’s evidence. Each service owns its own effect, so this pattern stops short of an end-to-end exactly-once guarantee.

Exercise the ambiguous cases before calling the interface reliable: lose the response after commit, race two attempts with one identity, restart between requests, change the arguments, expire recovery evidence, revoke access and make a newer edit. Check both the effect and the information returned. The visible timeout message should identify the operation, state what remains unknown and offer the safe recovery query. That gives the person and the model an honest next step while software retains responsibility for the action’s state.

Sources

  1. RFC 9110: HTTP SemanticsIETF / RFC Editor · June 2022Verified Oct 26, 2026

    Retry semantics and If-Match; application recovery behavior is a design recommendation.

  2. OpenAI: Function callingOpenAIVerified Oct 26, 2026

    Application-owned execution in the tool-calling flow.

  3. OpenAI: Async tool callingOpenAIVerified Oct 26, 2026

    Original call_id correlates a tool output with a call; no business-operation idempotency guarantee is inferred.

  4. Amazon Builders’ Library: Making retries safe with idempotent APIsAmazon Web ServicesVerified Oct 26, 2026

    Caller intent, parameter mismatch, atomic token/effect processing and service-specific retention.

  5. PostgreSQL 18: TransactionsPostgreSQL Global Development GroupVerified Oct 26, 2026

    Local database transaction atomicity; no remote-effect or tested crash guarantee.

  6. PostgreSQL 18: Unique constraintsPostgreSQL Global Development GroupVerified Oct 26, 2026

    Composite uniqueness. The example specifies non-null operation identifiers.

Read next