fix(client): honor Retry-After delays up to two minutes - #3555
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e55b90fed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return timeout if timeout >= 0 else 0 | ||
|
|
||
| def _should_retry(self, response: httpx.Response) -> bool: | ||
| retry_after = self._parse_retry_after_header(response.headers) |
There was a problem hiding this comment.
Ignore unconvertible Retry-After dates before retry checks
When automatic retries are enabled, a non-retryable error such as HTTP 400 can now raise ValueError or OverflowError instead of APIStatusError if it carries a syntactically parseable but out-of-range date, such as Retry-After: Fri, 29 Sep 100000 16:26:57 GMT: parsedate_tz() accepts it, but mktime_tz() in _parse_retry_after_header() fails. Previously _should_retry() did not parse this header for statuses that would not be retried, so conversion failures should be treated as malformed header values rather than escaping from this new call.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch. parsedate_tz() can accept dates that mktime_tz() cannot represent, so I updated _parse_retry_after_header() to treat TypeError, ValueError, OverflowError, and OSError as malformed header values. I also added sync and async regressions confirming that an HTTP 400 with this date still raises APIStatusError after one request, plus timeout-calculation coverage for the fallback behavior. The fix is committed locally as 8d149be8 and has not been pushed yet.
HAYDEN-OAI
left a comment
There was a problem hiding this comment.
Reviewed the current head and found no substantive correctness or compatibility issues. The sync and async retry paths consistently honor finite server-directed delays through 24 hours, reject longer delays without retrying early, and preserve fallback/status-error behavior for malformed or non-finite values.
Summary
Retry-Afterdelays up to 120 secondsMotivation
The Python SDK currently ignores
Retry-Aftervalues above 60 seconds and falls back to a much shorter exponential delay. Historical reset-header data shows that common minute-scale waits can land just above 60 seconds, while extending the ceiling beyond two minutes adds little coverage before waits jump to hours or days.Developer impact
Clients with automatic retries enabled will honor server-directed delays through two minutes. If
Retry-Afterexceeds two minutes, the SDK surfaces the API error instead of blocking a synchronous worker for an extended period or retrying before the requested time. Retry attempts remain bounded bymax_retries.Validation
.venv/bin/ruff format --check src/openai/_constants.py src/openai/_base_client.py tests/test_client.py.venv/bin/ruff check src/openai/_constants.py src/openai/_base_client.py tests/test_client.py.venv/bin/pytest -q tests/test_client.py(198 passed, 2 skipped)