openai / openai/codex

Python SDK: retry_on_overload jitter can push sleep beyond max_delay_s

Open Beginner friendly
#41,024 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

app-server bug
Dominant language
Rust
Stars
125k
Forks
19.5k
PR merge metrics
PR metrics pending

Description

Description

retry_on_overload() in sdk/python/src/openai_codex/retry.py computes the jitter window from the uncapped delay, but adds it to the capped base:

delay = initial_delay_s
attempt = 0
while True:
    attempt += 1
    try:
        return op()
    except Exception as exc:
        ...
        jitter = delay * jitter_ratio
        sleep_for = min(max_delay_s, delay) + random.uniform(-jitter, jitter)
        if sleep_for > 0:
            time.sleep(sleep_for)
        delay = min(max_delay_s, delay * 2)

Nothing in the function validates initial_delay_s <= max_delay_s (only max_attempts >= 1 is checked), and this is a public, documented function (openai_codex.retry_on_overload, also used internally by CodexClient.request_with_retry_on_overload / AsyncCodexClient.request_with_retry_on_overload, both of which expose initial_delay_s/max_delay_s as caller-tunable kwargs with no cross-validation either). If a caller passes initial_delay_s > max_delay_s, the first sleep's jitter is computed from the uncapped delay, so the actual sleep can exceed the documented max_delay_s ceiling.

Steps to reproduce
import random, time
from openai_codex.retry import retry_on_overload
import openai_codex.retry as retry_mod

retry_mod.is_retryable_error = lambda exc: True  # force retry path

calls = []
time.sleep = calls.append

attempts = {"n": 0}
def op():
    attempts["n"] += 1
    if attempts["n"] < 3:
        raise RuntimeError("boom")
    return "ok"

random.seed(1)
retry_on_overload(op, max_attempts=3, initial_delay_s=10.0, max_delay_s=2.0, jitter_ratio=0.2)
print(calls)

Output:

[0.5374569764496049, 2.277946989549786]

The second sleep (2.2779...) exceeds the configured max_delay_s=2.0.

Expected behavior

No sleep should ever exceed max_delay_s, regardless of initial_delay_s.

Actual behavior

The first sleep can exceed max_delay_s when initial_delay_s > max_delay_s, because the jitter window is derived from the uncapped delay while the base is capped.

Environment
  • Commit: 7c37479 (main, 2026-08-27), Python 3.14
  • File: sdk/python/src/openai_codex/retry.py:37-38
  • Note: retry_on_overload currently has no dedicated unit test file (only referenced in tests/test_public_api_signatures.py for signature checks, plus docs/examples) — this edge case isn't covered anywhere.
Suggested fix
-            jitter = delay * jitter_ratio
-            sleep_for = min(max_delay_s, delay) + random.uniform(-jitter, jitter)
+            base_delay = min(max_delay_s, delay)
+            jitter = base_delay * jitter_ratio
+            sleep_for = base_delay + random.uniform(-jitter, jitter)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in sdk/python/src/openai_codex/retry.py at retry_on_overload and review the documented retry parameters and current sleep calculation. Check tests/test_public_api_signatures.py for existing coverage, then add a regression test for initial_delay_s greater than max_delay_s. Done means retries never sleep longer than the configured max_delay_s while existing retry behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.