## TL;DR
A cell that raises the same exception with the same message twenty times will raise it the twenty-first time; retrying is not a strategy for deterministic errors. Classify each failure as transient (timeouts, rate limits, connection drops) or deterministic (NameError, TypeError, bad SQL, missing file) and retry only the transient kind, with a capped budget and backoff. When the budget is spent, the agent must stop, report the error, and change the code or inputs instead of looping.

```text
agent retried a failed cell 20 times: infinite retry on a deterministic error
```

## Use this when
- An agent loop re-executes a failing cell dozens of times with an identical traceback.
- CI or scheduled runs burn hours re-running a notebook that cannot succeed.
- Logs show the same exception type and message on every attempt.

## Not for this skill when
- The cell fails intermittently (flaky network, throttled API). That is a transient failure where bounded retries are correct.
- The kernel itself crashes and needs restarting. That is a kernel lifecycle policy, a different fix.

## Steps
1. Stop the loop and capture one full traceback. Expected: a single representative error instead of twenty identical ones.
2. Classify the error: transient means the same code could succeed later (timeout, 429, connection reset); deterministic means the code or inputs are wrong (NameError, SyntaxError, 404 on a bad path, SQL error). Expected: a written classification for the error at hand.
3. Fix the deterministic cause directly: correct the code, the query, or the input path, then run the cell once. Expected: the cell passes on the first run after the fix.
4. Add a retry policy to the agent: maximum 3 attempts for transient errors with exponential backoff, zero retries for deterministic errors. Expected: the policy is in the agent's instructions, not just in your head.
5. Make the agent compare tracebacks across attempts: if the exception type and message are identical twice in a row, stop retrying and escalate. Expected: the loop terminates itself on the second identical failure.
6. Log every retry with the attempt number and classification so the next incident is diagnosable. Expected: logs show why each retry happened, not just that it did.

## Tool compatibility
Any agent harness executing notebooks via nbclient, papermill, or kernel APIs; Python 3.9+.

## Variant phrasings
### agent stuck retrying failing notebook cell
The loop phrasing. Classify first; deterministic errors get zero retries.

### infinite retry loop jupyter agent
The general phrasing. A retry budget with identical-traceback detection ends it.

### cell keeps failing agent keeps rerunning
The symptom phrasing. The twentieth identical traceback is the signal to stop.

## Why it happens
Retry logic exists for a world where failures are random, but most notebook cell failures are deterministic: the code is wrong, the file is missing, or the query is invalid, and no amount of waiting changes that. Agents without an error classifier treat every failure as transient because their retry wrapper was written for network calls. The result is a loop that burns compute and, worse, can burn money against metered APIs. Identical exception type plus identical message across attempts is a cheap, reliable determinism signal that any harness can implement.

## Edge cases / pitfalls
- A retry budget alone does not fix the bug; it only bounds the waste. The deterministic error still needs a code fix.
- Some errors look transient but are deterministic (a 429 caused by the agent's own tight loop). Classify by cause, not just by status code.
- Backoff without jitter can synchronize retries across parallel agents into thundering herds. Add jitter to the backoff.
- Agents that 'fix' code by guessing and re-running need the same budget: cap fix attempts, then escalate to a human.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_DhqeVqNv10jh-uy5QHDAeA
