codex-client/retry: TransportError::RetryLimit is unreachable — retry exhaustion surfaces wrong error
What issue are you seeing?
TransportError::RetryLimit is defined and matched throughout the codebase (in api_bridge.rs it becomes CodexErr::RetryLimit → ResponseTooManyFailedAttempts), but it can never actually be emitted by run_with_retry.
File: codex-rs/codex-client/src/retry.rs, lines 58–72
The loop is for attempt in 0..=policy.max_attempts (inclusive upper bound). On the final iteration where attempt == max_attempts, should_retry returns false (its own guard: if attempt >= max_attempts { return false; }), so the match falls to Err(err) => return Err(err) — returning the raw underlying error. The Err(TransportError::RetryLimit) after the loop is dead code; the loop always exits via an early return before reaching it.
Impact: After exhausting all retries, callers receive a raw Http 5xx or TransportError::Network instead of TransportError::RetryLimit. The match arm for RetryLimit in api_bridge.rs (line 122) is therefore also dead. Users never see the ResponseTooManyFailedAttempts error the system was designed to surface.
What steps can reproduce the bug?
This is a code-reading bug — no runtime reproduction needed. Read codex-rs/codex-client/src/retry.rs:
run_with_retryloopsfor attempt in 0..=policy.max_attempts- On the last iteration (
attempt == max_attempts),should_retryreturnsfalse - The
Err(err) => return Err(err)branch fires, returning the raw error Err(TransportError::RetryLimit)at line 72 is never reached
Proposed fix (one character): Change 0..=policy.max_attempts to 0..policy.max_attempts
// Before
for attempt in 0..=policy.max_attempts {
// After
for attempt in 0..policy.max_attempts {
This makes the loop run exactly max_attempts times, and when exhausted, falls through to Err(TransportError::RetryLimit) as intended.
What is the expected behavior?
After exhausting all retries, run_with_retry should return Err(TransportError::RetryLimit). This would then be correctly converted by api_bridge.rs into CodexErr::RetryLimit → ResponseTooManyFailedAttempts, giving users a clear, actionable error message instead of a raw 5xx or network error.
Additional information
The fix is a single character change — ..= to .. on the for loop range in run_with_retry. No other files need to change. The existing RetryLimit handling in api_bridge.rs and protocol/src/error.rs is already correct and will work properly once this is fixed.