Skip to content

[Bug]: deterministic request errors are re-sent up to max_attempts in the retry ladder #2690

Description

@1688mengdie

Problem

RoundExecutor replays every provider-request error up to max_attempts based
only on a budget check (local_attempt_index < max_attempts - 1). Deterministic
request failures — 4xx statuses and context_length_exceeded — are re-issued in
full even though retrying them cannot succeed, so a single bad request can be
re-sent the whole configured number of times, amplifying latency and cost before
any error is surfaced.

Root cause

The retry decision does not consult the error category. Both the request_error
(C1) and stream_error (C5) gates classify the category only after the budget is
exhausted, when they already have to build the terminal error. The retry-vs-terminal
semantic already exists in is_retryable_category (Network / RateLimit / Timeout /
ProviderUnavailable are retryable; everything else is terminal), but it is private
to core-types and is not consulted for admission — only for the final classification
after the budget gate has already been passed.

Impact

A deterministic error (for example a 401 auth, 413 payload-too-large, or
context_length_exceeded request) is retried the full number of times even though
every attempt fails the same way. This wastes the attempt budget and the caller's
time on an error that will never recover, and it delays the actual error handling
until the budget is exhausted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions