01 · THE CLAIM"Timed out" means "we don't know yet."
When an agent's tool call times out, it's tempting to log FAILED and try again. But the save, the payment or the ticket may already exist on the other side. Retry blind and you can do the work twice. Our claim: a timeout is its own state, and the next step is to check the record, not to repeat the call.
A timeout tells you the clock ran out, not what happened.
02 · WHAT HAPPENEDWe wrote the gap down twice.
In "Saved" is a claim we built a read-back check: after an agent says "saved", look the record up again and mark it VERIFIED or NOT VERIFIED. A reader, @QuantaCortex, pointed out on X that a save still in flight fits neither label, and we agreed in public. A day later, the honest limits in An approval is for one exact change said it again: "It doesn't reconcile lost responses yet." This is the piece that fills that gap.
03 · WHAT WE READTwo sources, one warning.
- AWS Builders' Library, Marc Brooker, "Timeouts, retries, and backoff with jitter". "A timeout or failure doesn't necessarily mean that side effects haven't happened." And: "APIs with side effects aren't safe to retry unless they provide idempotency." In plain words, only retry if the other side can tell a repeat from a new request.
- Retries multiply. The same guide shows a request passing through five layers of services. "If each layer retries independently, the load on the database will increase 243x." To pick a timeout, AWS starts from how slow the other service really is (for example its slowest 0.1% of responses), not from a guess.
- QuantaCortex, "Resume Interrupted AI Work Without Repeating Completed Actions" (21 October 2026). "Before asking the assistant to continue, reconstruct the task from what exists now," keep what's finished, and "leave uncertain actions paused until someone can resolve them." We quote the free preview only; the full checklist is behind a free sign-up we haven't read.
The pattern: the risky moment isn't the timeout. It's the retry that comes right after it.
04 · THE CHECKTimed out, read the record, then decide.
Same method as "The plan is not the trace", "Saved" is a claim, "Yes to all" is not a yes and An approval is for one exact change: write the rule, run it in the terminal, keep the output. This is the check, copied from reconcile.py:
def reconcile(run_id, store, has_dedupe_id, retries_used):
"""Decide what to do after a tool call timed out. Returns (decision, reason)."""
try:
record = store.lookup(run_id) # read the record by this run's ID
except ConnectionError:
return "PAUSED", "can't read the record; a person decides"
if record is not None:
return "DONE", "record found; the work already happened"
if not has_dedupe_id:
return "PAUSED", "no duplicate guard; a retry could do it twice"
if retries_used >= 1:
return "PAUSED", "already retried once; a person decides"
return "RETRY ONCE", "record missing and safe to repeat"Excerpt from our demo file. The full file is 42 lines of standard-library Python.First, check the checker. Then run five timeouts through it.
$ python3 -m unittest test_reconcile ............ ---------------------------------------------------------------------- Ran 12 tests in 0.000s OKCopied from our run (2 Nov 2026). Text unchanged.
$ python3 reconcile.py save timed out, record exists -> ('DONE', 'record found; the work already happened') save timed out, record missing -> ('RETRY ONCE', 'record missing and safe to repeat') same run, after one retry -> ('PAUSED', 'already retried once; a person decides') payment, no duplicate guard -> ('PAUSED', 'no duplicate guard; a retry could do it twice') record store unreachable -> ('PAUSED', "can't read the record; a person decides") retried twice with the same ID -> 1 record blind retries, 5 layers x 3 tries -> 243 calls at the bottom (AWS's example) note: made-up records; DONE means the record exists, not that it is correct.Copied from our run (2 Nov 2026). Text unchanged.
One DONE, one RETRY ONCE, three PAUSED, and no FAILED. The first line is the case that matters most: the save had already happened, so a blind retry would have made a second ticket. One test runs every combination and checks that a timeout alone never becomes FAILED. Another checks that saving twice with the same ID still leaves one record, which is what makes the single retry safe.
05 · HOW THE CHECK DECIDESRead in order. First match wins.
| AFTER A TIMEOUT | RESULT |
|---|---|
| The record can't be read (store unreachable) | PAUSED |
| The record exists for this run ID | DONE |
| Missing, and the action has no ID that stops duplicates | PAUSED |
| Missing, and we already retried once | PAUSED |
| Missing, safe to repeat, first time | RETRY ONCE |
Three ways to pause and one way to retry is deliberate, like the read-back check and the approval receipt. An unclear timeout costs a person a minute, not a double payment.
06 · WHY ONCERetries stack up fast.
This is AWS's arithmetic, not a measurement of ours. Stacked retries turn one slow answer into hundreds of calls on the system that was already struggling. Reading the record first, and retrying once at one layer, keeps a timeout from becoming an outage.
07 · WHAT THIS DOES NOT PROVEHonest limits.
- DONE means a record exists, not that it's right. Checking the content is the read-back check's job from "Saved" is a claim.
- It doesn't choose how long to wait. The demo starts after the timeout. Picking the timeout is still open.
- A duplicate guard only works if the other side honours it. In the demo, our pretend store does. A real service has to support it.
- It's a small local demo. Made-up records, 42 lines of Python. Nothing in our real agents runs it yet.
| Read-back check: VERIFIED or NOT VERIFIED after a fresh lookup | BUILT |
| Written agent rule: before resuming interrupted work, sort it into keep, may resume and unresolved; every action carries an ID that stops duplicates | BUILT |
| Reconcile demo: DONE, RETRY ONCE or PAUSED, never FAILED on a timeout, 12 tests OK | RAN |
| WAITING and TIMED_OUT as real run states in our agents | PLANNED |
| Reconcile wired into our agents, with one log line per timeout | PLANNED |
| How long "waiting" should last | UNKNOWN |
"Built" here means a written rule our agents work under and the read-back demo. The rule is not enforced by code yet.
08 · WHAT WE'LL BUILDReconcile goes into the harness.
This is the next piece of the approval gate from the last two write-ups. The named yes and the receipt come before an action. Reconcile and read-back come after it:
- Named yes for one change (from "Yes to all" is not a yes).
- Receipt spent once, after re-checking the live state (from An approval is for one exact change).
- Act once, with a run ID and an ID that stops duplicates.
- If it times out: reconcile, read the record, then DONE, RETRY ONCE or PAUSED (this article's check).
- Read back the record and mark it VERIFIED or NOT VERIFIED (from "Saved" is a claim).
09 · TAKEAWAYCheck the record before you retry.
A timeout tells you the clock ran out, not what happened. Read the record by run ID. If it's there, you're done. If it's missing and safe to repeat, retry once. Anything else waits for a person. We still haven't decided how long "waiting" should last: too short and slow work gets marked TIMED_OUT for nothing, too long and a stuck run holds everything up. Feel free to challenge our thinking. How long does your agent wait before it calls a timeout?
TRY IT YOURSELFTwo checks for your next timeout.
- Find one place your agent retries on a timeout. Does it read the record first?
- Does that action carry an ID that stops it from running twice?
- Earlier write-ups: "Saved" is a claim · An approval is for one exact change.
- The reconcile demo is a small local teaching example: made-up records, standard-library Python. The tests and output are from our run on 2 November 2026.
