Skip to content

Honor retry-after headers when OpenAI requests a retry - #1796

Open
1fanwang wants to merge 2 commits into
temporalio:mainfrom
1fanwang:1fannnw/openai-retry-after-should-retry
Open

Honor retry-after headers when OpenAI requests a retry#1796
1fanwang wants to merge 2 commits into
temporalio:mainfrom
1fanwang:1fannnw/openai-retry-after-should-retry

Conversation

@1fanwang

@1fanwang 1fanwang commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

When OpenAI answers an agent activity with x-should-retry: true and a retry-after header, the SDK drops that delay and uses the activity's configured interval. A server asking for 5 seconds can therefore get another request after 1 second. This change honors the server delay. Users can also opt out through RetryPolicy(non_retryable_error_types=["APIStatusError"]), including for concrete errors such as RateLimitError.

The status handler now converts every APIStatusError subclass to an ApplicationError whose type remains APIStatusError. The same raise carries the retry decision and parsed delay, so the explicit true and false headers still take precedence over the HTTP status.

How I tested it

Before: retry delay was lost
git checkout 35b24bb28edf40fc3e2d3c446ea77a0cba312f6f -- temporalio/contrib/openai_agents/_invoke_model_activity.py
uv run pytest tests/contrib/openai_agents/test_openai.py -k "retry_after or should_retry" -q --no-header

E           openai.APIStatusError: Something went wrong.
temporalio/contrib/openai_agents/_invoke_model_activity.py:315: APIStatusError
Results (1.19s):
         3 failed
         2 passed
        54 deselected
Before: the converted error had no type
git checkout c5724638be1d65c271ca3872138e3b176b4b2dd6 -- temporalio/contrib/openai_agents/_invoke_model_activity.py
uv run pytest tests/contrib/openai_agents/test_openai.py::test_retry_after_ms_propagated_when_server_requests_retry -q --no-header

E       assert None == "APIStatusError"
After
git checkout HEAD -- temporalio/contrib/openai_agents/_invoke_model_activity.py
uv run pytest tests/contrib/openai_agents/test_openai.py -k "exception_handling or retry_after" -q --no-header

....                                                                     [100%]
Results (16.15s):
         4 passed
        55 deselected
         3 warnings

The warnings are the existing workflow sandbox notices for pydantic_core imports.

The delay parsed from retry-after-ms and retry-after was discarded on
the x-should-retry: true path, which re-raised the raw error instead of
an ApplicationError carrying next_retry_delay. Fold the header into the
shared retryable computation so every path carries the delay.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
@1fanwang
1fanwang requested review from a team as code owners August 29, 2026 07:51

@brianstrauch brianstrauch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR! Can we preserve APIStatusError instead of returning an untyped ApplicationError? I should be able to opt out of retry with RetryPolicy(non_retryable_error_types=["APIStatusError"]).

Signed-off-by: 1fanwang <1fannnw@gmail.com>

@brianstrauch brianstrauch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

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.

2 participants