Opening the page…

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.
Lantern Bloom@yuruyurau ↗

A timeout tells you that the caller stopped waiting. It does not establish whether the requested action happened. For a tool that changes state, the useful next question is: what can the software prove about this particular operation? A model's guess, a fresh request identifier or a reassuring message cannot supply that proof.

Consider an illustrative bookmark application. A person asks an assistant to mark a saved link as reviewed. The application submits the action, the database commits it, and the connection breaks before the response arrives. The assistant sees a timeout. Meanwhile, the person edits the bookmark again. A recovery path must identify the earlier action without repeating it or overwriting that newer work. This is an authored example, not a report about a deployed product.

HTTP already distinguishes requests that can safely be repeated from those that need additional knowledge. RFC 9110 says clients should not automatically retry a non-idempotent request unless they know its semantics are idempotent or can establish that the first request was never applied. A missing response alone supplies neither fact. HTTP semantics

Method & limits

Design note: Primary documentation checked October 26, 2026. The bookmark example and pseudocode are authored illustrations, not a deployed product, executed implementation or reliability benchmark.

Give the operation an 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 that operation identity before the first attempt and retain it through recovery. Scope it to the authenticated account, and bind it to validated intent: the action, target, expected revision and relevant arguments. Derive the account from authenticated server context rather than trusting an account identifier supplied by the model. Reusing an identity with different intent should produce a conflict. AWS describes caller-provided request identifiers and parameter-mismatch checks for this purpose. A hash of matching arguments alone is insufficient to express intent: a person may deliberately request the same action twice. AWS on idempotent APIs

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

Commit the effect and its receipt together

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 following pseudocode sketches first execution and replay inside a valid recovery context. Establish that context before the first attempt; an expired context must be rejected before an identifier can be claimed again. The unique-operation claim must handle concurrent attempts, and the revision comparison must be part of the database update. Only a newly claimed operation proceeds to the mutation; an existing pending operation must wait or conflict. Concrete SQL still 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.

Resolve the original operation

Recovery should query the same operation identity. A retained committed receipt supports reporting that the earlier action was applied. An operation still running calls for observation rather than a second mutation. A confirmed rejection before execution can support a retry within the contract. An unavailable or ambiguous result remains unknown. These states deserve different machine-readable results; collapsing them into a generic error forces the model or interface to invent 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.

The crawl frontier’s commit boundary applies the same distinction between a completed effect and an acknowledgment to worker scheduling.

Preserve the newer state

A historical receipt also differs from the state the caller may see now. Suppose the first action committed revision 13, then the person made a new review decision at revision 14. The receipt can establish what happened at revision 13. It cannot justify restoring that old state or describing it as current. Conditional writes help preserve newer work; HTTP's If-Match precondition serves a related purpose for representations that expose 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.

Keep the boundary explicit

The transaction above covers writes inside one database. It does not atomically commit an email, payment or other external service's action. A workflow crossing that boundary needs the other service's documented recovery contract and a design for reconciling partial progress. A local receipt cannot manufacture certainty about an external effect it never verified, and this pattern is not 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 Semantics

    IETF / RFC Editor · June 2022

    Verified Oct 26, 2026

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

  2. OpenAI — Function calling

    OpenAI

    Verified Oct 26, 2026

    Application-owned execution in the tool-calling flow.

  3. OpenAI — Async tool calling

    OpenAI

    Verified 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 APIs

    Amazon Web Services

    Verified Oct 26, 2026

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

  5. PostgreSQL 18 — Transactions

    PostgreSQL Global Development Group

    Verified Oct 26, 2026

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

  6. PostgreSQL 18 — Unique constraints

    PostgreSQL Global Development Group

    Verified Oct 26, 2026

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

Read next