Skip to content

fix(llm, openrouter): Report mid-stream provider errors - #978

Open
JeanMertz wants to merge 2 commits into
mainfrom
openrouter-mid-stream-errors
Open

fix(llm, openrouter): Report mid-stream provider errors#978
JeanMertz wants to merge 2 commits into
mainfrom
openrouter-mid-stream-errors

Conversation

@JeanMertz

Copy link
Copy Markdown
Collaborator

A request that OpenRouter rejects after committing the response cannot change its HTTP status, so the rejection arrives as an ordinary SSE chunk carrying a top-level error next to an empty choices array. The ChatCompletion response type had no error field, so serde discarded the payload and the chunk mapped to zero events. The turn loop then saw a stream that ended without a terminal event, classified the synthetic sentinel as transient, and burned the full retry budget resending an identical body before failing with "provider stream ended without a terminal event".

An oversized prompt now fails once with what the provider actually said:

error  API error (status 400): prompt is too long:
       1284668 tokens > 1000000 maximum

Both error paths route through one map_error helper reusing the existing From<jp_openrouter::Error> classification, so a status code means the same thing wherever it arrives. The per-choice path previously sniffed the message text for quota patterns and fell back to Other for everything else, which made a mid-stream 503 non-retryable while an identical 503 at connect time was retryable. Both are transient now, and a 400 is not retryable on either path.

A request that OpenRouter rejects after committing the response cannot
change its HTTP status, so the rejection arrives as an ordinary SSE chunk
carrying a top-level `error` next to an empty `choices` array. The
`ChatCompletion` response type had no `error` field, so serde discarded
the payload and the chunk mapped to zero events. The turn loop then saw a
stream that ended without a terminal event, classified the synthetic
sentinel as transient, and burned the full retry budget resending an
identical body before failing with "provider stream ended without a
terminal event".

An oversized prompt now fails once with what the provider actually said:

    error  API error (status 400): prompt is too long:
           1284668 tokens > 1000000 maximum

Both error paths route through one `map_error` helper reusing the existing
`From<jp_openrouter::Error>` classification, so a status code means the
same thing wherever it arrives. The per-choice path previously sniffed the
message text for quota patterns and fell back to `Other` for everything
else, which made a mid-stream 503 non-retryable while an identical 503 at
connect time was retryable. Both are transient now, and a 400 is not
retryable on either path.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant