Skip to content

fix(openai): carry a 429's Retry-After into the provider error - #28

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
shaurya703:fix/openai-retry-after
Sep 24, 2026
Merged

senamakel merged 1 commit into
tinyhumansai:mainfrom
shaurya703:fix/openai-retry-after

Conversation

@shaurya703

Copy link
Copy Markdown
Contributor

Summary

On the OpenAI-compatible transport, a 429's Retry-After header never reached the retry layer. provider_error hardcoded retry_after_ms: None, and by the time the error was built the response's headers were already gone: checked_response called response.text() first. The Anthropic transport reads the header before consuming the body and fills the field from it, so a server-supplied wait was honoured on one provider and silently dropped on the other. Reported downstream as tinyhumansai/openhuman#6504, where this is the managed-backend path.

Behavior change

  • OpenAiModel::checked_response reads Retry-After before text() and passes it into parse_error_body.
  • parse_error_body and provider_error take a retry_after: Option<&str> and set retry_after_ms through the shared tinyinference_core::parse_retry_after_ms (delta-seconds or HTTP-date, capped at MAX_BACKOFF_MS), the same call the Anthropic transport makes.
  • Transport failures (no response) and mid-stream error events (no headers) pass None, so their errors are unchanged.
  • No public API change: both functions are crate-private.

Verification

cargo fmt --all -- --check
cargo clippy -p tinyinference-llm --tests
cargo test -p tinyinference-llm --lib -- providers::openai   # 177 passed

New test parse_error_body_carries_retry_after_into_the_structured_error: a 429 with Retry-After: 7 yields retry_after_ms == Some(7000); the same body with no header, or with an unparseable value, yields None. The header-present case fails on main (Some(7000) vs None).

The OpenAI-compatible transport built its `ProviderError` with
`retry_after_ms: None`, and the response's headers were gone by the time
the error was built because `text()` had already consumed it. The retry
layer prefers `retry_after_ms` over its own backoff curve, and the
Anthropic transport already fills it from the same header, so a
server-supplied wait was honoured on one provider and silently dropped on
the other.

Read `Retry-After` before consuming the body, thread it through
`parse_error_body` into `provider_error`, and parse it with the shared
`parse_retry_after_ms`. Transport failures and mid-stream error events
have no response headers to read, so they pass `None` as before.

Refs tinyhumansai/openhuman#6504
@tinysweeper

tinysweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 6 lane(s) and found 0 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Incomplete
Priority: none
Reviewed head: 6f6ffef3e8f1
Updated: 1790241305 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 1 Active findings 0
Tests 1 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 1

Completeness: Incomplete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

  • Unreviewed: tinysweeper/tests

Findings

No active actionable findings.

Could not review: tinysweeper/tests

Before merge

  • Complete the tests review for tinysweeper/tests.

How this fits together

flowchart LR
  n0["...dy_classifies_retryability_by_http_status<br/>changed"]:::changed
  n1["OpenAiModel<br/>changed"]:::changed
  n2["responses_sse_failure<br/>changed"]:::changed
  n3["model"]:::impacted
  n4["translate_request"]:::impacted
  n5["ModelRequest"]:::impacted
  n6["...ne_knobs_default_to_supported_wire_shapes"]:::impacted
  n7["translates_request_to_openai_json_shape"]:::impacted
  n0 -->|calls| n3
  n0 -->|tests| n3
  n2 -->|uses| n1
  n3 -->|uses| n1
  n4 -->|uses| n5
  n6 -->|calls| n3
  n6 -->|tests| n3
  n6 -->|calls| n4
  n6 -->|tests| n4
  n7 -->|calls| n3
  n7 -->|tests| n3
  n7 -->|calls| n4
  n7 -->|tests| n4
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change correctly propagates the HTTP `Retry-After` header into normalized OpenAI provider errors, including transport error handling and tests. It looks safe to merge. _The code index is behind this pull request (indexed at `6081aefb7c8f`), so retrieved context may be out of date._ _Memory was unavailable (model: cortex: v1/recall: timed out after 10s), so this review ran without it._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change propagates HTTP Retry-After values into normalized OpenAI provider errors without exposing secrets or widening any trust boundary. It looks safe to merge. _The code index is behind this pull request (indexed at `6081aefb7c8f`), so retrieved context may be out of date._ _Memory was unavailable (model: cortex: v1/recall: timed out after 10s), so this review ran without it._

tests

  • Conclusion: Neutral
  • Scope reviewed: incomplete; unanswered: tinysweeper/tests
  • Lane summary: No reviewer could be consulted.

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: This change correctly passes the `Retry-After` header from a 429 response through to the `ProviderError` in the OpenAI transport, matching the behavior already present in the Anthropic transport. The code is clean, the tests are thorough, and no bugs or regressions are introduced by the diff itself. It is safe to merge. _The code index is behind this pull request (indexed at `6081aefb7c8f`), so retrieved context may be out of date._ _Memory was unavailable (model: cortex: v1/recall: timed out after 10s), so this review ran without it._

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: No end-to-end harness in this repository: no e2e test files and no e2e workflow.
Evidence and run details
  • Models: ladder/vectors, gpt-5.6-luna, deepseek/deepseek-v4-flash
  • Spend: $0.002180
  • Tokens: 78834 input · 3240 output · 3996 cached · 346 embedding
Head State Pass summary
6f6ffef3e8f1 incomplete 0 active finding(s), 0 resolved finding(s) (at 1790241305)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 9dcb951f-10b7-4db7-a4e1-c43675e8e516


Comment @coderabbitai help to get the list of available commands.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: tinysweeper/tests.

             $0.0022 · 78,834 in / 3,240 out · 3,996 cached (5%) · ladder/vectors, gpt-5.6-luna, deepseek/deepseek-v4-flash · 346 embedded
critique:    $0.0005 · 35,508 in / 702 out   · 2,120 cached (6%) · gpt-5.6-luna
security:    $0.0005 · 35,020 in / 541 out   · 1,876 cached (5%) · gpt-5.6-luna
description: $0.0005 · 4,990 in  / 86 out    · 0 cached (0%)     · deepseek/deepseek-v4-flash

@senamakel
senamakel merged commit 9c5b411 into tinyhumansai:main Sep 24, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants