Loading
0x40Lesson 5 of 8

Control stopping, retries, and recovery

Bound the loop, detect when it’s stuck, retry safely with backoff and idempotency, and hand off cleanly.

25 min 6-question quiz 3 code exercises
By the end of this lesson you can
  • Set step budgets and detect stuck loops
  • Retry transient failures with exponential backoff and jitter
  • Make side-effecting actions safe to retry with idempotency keys

Every loop needs answers to three questions: How do we know we’re done? When do we give up? What happens after a failure?

  • Budgets - maximum steps, time and spend. Hitting one isn’t a crash: save progress and hand back a clear summary.
  • Stuck detection - the same action giving the same result again and again means the agent isn’t making progress.
  • Verified completion - stop on ground truth (tests pass), not on the model saying “done”.

Try it

Up to 15 XP

Loop simulator

Replay two scripted runs. “Fix the bug” succeeds on its own. “Ship the flaky fix” gets stuck - then skips the test and deploys. Run it with no controls, then try a step budget of 4, stuck detection, and the approval gate. Which control catches what?

Goal: Make the test suite pass.

  1. 1readrun_tests()
    Thought: See what fails first.→ 2 failing: test_total, test_discount
  2. 2readread_file("cart.py")
    Thought: Both involve discounts - read the cart code.→ discount applied twice on line 42
  3. 3writeedit_file("cart.py", line=42)
    Thought: Remove the second application.→ edit applied
  4. 4readrun_tests()
    Thought: Verify with the tests, not my own claim.→ all 42 tests pass
Goal met - the loop stopped on a verified success.

Retries done right

Retry only failures that may be transient (timeouts, rate limits, 503s) - not a 400 “invalid argument”. Wait longer each time with exponential backoff (0.5 s, 1 s, 2 s, 4 s… up to a cap), and add jitter - a random wait between 0 and the backoff - so many clients don’t retry in lockstep (AWS Architecture Blog).

Retrying an action with side effects is dangerous: a timeout doesn’t mean it failed - the payment may have gone through and only the response was lost. Send an idempotency key with the request; the server performs the action once per key and replays the original result for duplicates (Stripe’s API works this way).

backoff.py
base, cap = 0.5, 8
print([min(cap, base * 2 ** attempt) for attempt in range(6)])
Output
[0.5, 1.0, 2.0, 4.0, 8, 8]

Key takeaways

  • Bound steps, time and spend; stop on verified success or a clear handoff.

  • Detect stuck loops - repeated actions with repeated results.

  • Retry only transient errors, with capped exponential backoff and jitter; use idempotency keys for side effects.

Lesson quiz

6 questions · pass with 5 correct · up to 50 XP

Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.

Practice: write Python

Write Python in the editor and run it against sample inputs. Python runs locally in your browser using a WebAssembly runtime.

Exercise 1

Exponential backoff with full jitter

+25 XP

Input: ATTEMPTS BASE CAP. Call random.seed(42) once. For each attempt from 0, the backoff is min(CAP, BASE * 2 ** attempt) and the actual wait is random.uniform(0, backoff). Print attempt N: backoff B s, wait W s with B formatted :g and W to 2 decimals, numbering attempts from 1.

  • Six attempts
main.py
Loading editor…

Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.

Exercise 2

Detect a stuck loop

+25 XP

Each line is action|result. Stop as soon as the same action has produced the same result 3 times and print stuck at step N: ACTION keeps returning RESULT. If that never happens, print finished after N steps.

  • Stuck
  • Making progress
main.py
Loading editor…

Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.

Exercise 3

Idempotency keys for safe retries

+25 XP

Each line is KEY AMOUNT, a payment request (retries reuse the key). Print charged KEY AMOUNT the first time a key is seen, replayed KEY (no new charge) for a repeat with the same amount, and error: KEY reused with a different amount otherwise. Finish with total charged: T.

  • A retried timeout
main.py
Loading editor…

Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.

Questions about this lesson

Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.

Loading posts…