# Can a retry loop exceed its attempt budget?

> Trace an off-by-one retry loop, test the zero and three-attempt boundaries, and review a minimal JavaScript repair.

- Canonical HTML: [https://unrust.dev/guides/javascript-retry-budget-challenge](https://unrust.dev/guides/javascript-retry-budget-challenge)
- Markdown representation: [https://unrust.dev/guides/javascript-retry-budget-challenge/index.md](https://unrust.dev/guides/javascript-retry-budget-challenge/index.md)

Published: 2026-10-21
Published by Unrust. Scoring and editorial method: https://unrust.dev/help#methodology

## Direct answer

The loop checks the counter before incrementing it. Values 0, 1, 2 and 3 all satisfy attempts <= 3, so the function calls send four times. At a budget of zero it still calls once. Changing <= to < matches this contract. A different contract that defines retries after an initial attempt would need a different implementation.

## Read the contract before the patch

maxAttempts is a non-negative integer and is the total number of permitted send calls, including the first call. Stop immediately after a successful call. A budget of zero permits no call.

Predict the result before reading the repair. Use a small counterexample to separate the code’s behavior from the behavior you expected.

### Original teaching example

```javascript
function attemptDelivery(maxAttempts, send) {
  let attempts = 0;
  while (attempts <= maxAttempts) {
    attempts++;
    if (send()) return attempts;
  }
  return attempts;
}
```



## Compare the repair against the same inputs

The loop checks the counter before incrementing it. Values 0, 1, 2 and 3 all satisfy attempts <= 3, so the function calls send four times. At a budget of zero it still calls once. Changing <= to < matches this contract. A different contract that defines retries after an initial attempt would need a different implementation.

### Repair under the stated contract

```javascript
function attemptDelivery(maxAttempts, send) {
  let attempts = 0;
  while (attempts < maxAttempts) {
    attempts++;
    if (send()) return attempts;
  }
  return attempts;
}
```

**Execution-checked examples, 21 October 2026**

| Input | Original | Repair |
| --- | --- | --- |
| Budget 0, always false | 1 call | 0 calls |
| Budget 3, always false | 4 calls | 3 calls |
| Budget 3, first call succeeds | 1 call | 1 call |



## Extend the review

This synchronous teaching example does not implement backoff, cancellation, timeouts, idempotency or production network error handling.

Change one assumption, predict a new outcome, then run the example in your own environment. The public lab repository contains the snippets and their checks. These examples teach review reasoning; they do not measure coding ability.

- [All public lab challenges](https://unrust.dev/guides/public-code-review-challenges/index.md)

- [Runnable lab source and tests](https://github.com/yomariano/unrust-code-review-lab)
- [Download the teaching pack](https://unrust.dev/resources/code-review-lab.json)

## Make your prediction

With maxAttempts = 3 and send always returning false, how many calls does the original loop make?

- Four calls. The equality case admits one extra attempt.
- Three calls, because the budget variable is three.

**Reasoning:** The loop checks the counter before incrementing it. Values 0, 1, 2 and 3 all satisfy attempts <= 3, so the function calls send four times. At a budget of zero it still calls once. Changing <= to < matches this contract. A different contract that defines retries after an initial attempt would need a different implementation.

## Practice loop

1. State the input contract in your own words.
2. Predict each boundary result before running the code.
3. Compare the original and repair with exactly the same input.
4. Write one additional case and explain what it establishes.

## Common questions

### Is this a scored Unrust assessment?

No. This is a fully public, worked teaching example. Its answers are intentionally available and it produces no diagnostic score.

### Can I reuse the example in a tutorial?

Yes. The public lab source is MIT licensed. Keep the license, state the input contract and link to the example and its verification date.

## Limitations

This synchronous teaching example does not implement backoff, cancellation, timeouts, idempotency or production network error handling.

## Provenance

Original public teaching material authored for Unrust and execution-checked on 21 October 2026. It is separate from the diagnostic and Daily puzzle banks.

## Sources

- [while statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while) — MDN Web Docs. The loop condition is evaluated before each iteration.

## Related guides

- [Free code review challenges with explanations and runnable examples](https://unrust.dev/guides/public-code-review-challenges/index.md): Practice three small JavaScript, Python and TypeScript code reviews. Predict the behavior, inspect a repair and run the original public teaching examples.
- [How do you read code against its contract?](https://unrust.dev/guides/reading-code-against-a-contract/index.md): Learn how to compare implementation with intent, find mismatched assumptions, and catch bugs that clean-looking code can hide.
- [How do you review AI-generated code?](https://unrust.dev/guides/reviewing-ai-generated-code/index.md): Review a JavaScript bug that mishandles zero, compare the corrected code and boundary tests, and learn how to verify AI-generated changes against a contract.

Next step: [Take the free diagnostic](https://unrust.dev/diagnostic/index.md)
