openai / openai/codex

[Bug + Solution] Python SDK: overload retry gives up on the "retry later" response

Open
#46,878 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app-server bug
Dominant language
Rust
Stars
125k
Forks
19.5k
PR merge metrics
PR metrics pending

Description

What issue are you seeing?

I've found that the Python SDK doesn't retry the app-server's Server overloaded; retry later. response, even when using its overload-retry helper with max_attempts=3. It stops after the first request and raises CodexRpcError.

I've checked both CodexClient.request_with_retry_on_overload() and its async version, and they behave the same way. This is the error they don't recognise:

{"code": -32001, "message": "Server overloaded; retry later."}

For example, let's consider a service that sends several requests to Codex and uses this helper to handle temporary overload. If the app-server is briefly too busy to accept a request, I would expect the helper to wait and try again. However, with this response, the operation fails immediately and configuring retries doesn't help.

I reproduced this using the complete SDK and a local subprocess returning the same error as the native app-server. I also checked what happens if the test server adds data: {"codex_error_info": "server_overloaded"} to that response. In this case, the SDK recognises the metadata, raises ServerBusyError, and the helper retries successfully. So the retry logic works, but it relies on metadata that the native queue-overload response doesn't include.

What steps can reproduce the bug?

To make this easier to check, here is a small example. Run this from the Codex repository root in a Bash-compatible shell, using a Python environment with the SDK dependencies installed:

PYTHONPATH=sdk/python/src python - <<'PY'
from openai_codex.errors import map_jsonrpc_error, is_retryable_error
from openai_codex.retry import retry_on_overload

# Matches the queue-overload response in
# codex-rs/app-server-transport/src/transport/mod.rs
OVERLOADED_ERROR_CODE = -32001
OVERLOADED_ERROR_MESSAGE = "Server overloaded; retry later."

calls = 0

def operation():
    global calls
    calls += 1
    if calls == 1:
        raise map_jsonrpc_error(
            OVERLOADED_ERROR_CODE,
            OVERLOADED_ERROR_MESSAGE,
        )
    return "ok"

try:
    print(retry_on_overload(operation, max_attempts=3))
except Exception as exc:
    print(type(exc).__name__, is_retryable_error(exc), calls)
PY

Actual output: CodexRpcError False 1.

I would expect ok after two calls, since the operation is set up to succeed on the second one, but the helper stops after the first error.

What is the expected behavior?

I would expect the helper to recognise that Codex couldn't accept the request into its queue, and retry up to the configured limit. This should also work when the optional error data is missing or null.

At the same time, other errors shouldn't automatically trigger a retry. For example, if the connection closes without a response, the server may already have accepted the request, so retrying could duplicate the operation.

Additional information
Environment and versions

I reproduced this using the Python SDK from main (revision 3d5b66c655c1).

I've also checked the relevant SDK and server code at the current Codex CLI prerelease 0.156.0-alpha.9 (20 September 2026), and it is unchanged from the tested checkout.

I used Python 3.14.4 on NixOS 26.05, x86_64 Linux. These tests didn't use a model, account, or native binary.

Root cause analysis

I've traced this to a mismatch between the native server's overload response and the SDK's error classification:

  1. When the incoming queue is full, the server constructs an error response with code -32001, message Server overloaded; retry later., and no error data.
  2. The SDK passes this response to map_jsonrpc_error(). For code -32001, the mapper checks for overload metadata in data or a message containing "retry limit" or "too many failed attempts". The native response has no data, and its message, "Server overloaded; retry later.", matches neither phrase. So the mapper falls back to a generic CodexRpcError.
  3. retry_on_overload() calls is_retryable_error(). The exception is neither a ServerBusyError nor a JsonRpcError carrying overload metadata, so that check returns False. The helper immediately raises the exception without retrying, regardless of the configured max_attempts.

This also explains the working comparison. With data: {"codex_error_info": "server_overloaded"}, the mapper returns ServerBusyError in step 2, so the retry check in step 3 passes and the second request succeeds.

Suggested fix

The easiest fix appears to be adding data: {"codex_error_info": "server_overloaded"} to the native queue-overload response. The SDK already recognises this metadata and retries correctly.

However, this wouldn't help clients connecting to older servers, which still omit the metadata. To support those responses as well, I'd suggest adding a compatibility check in map_jsonrpc_error(): recognise code -32001 together with the exact message "Server overloaded; retry later." when data is missing or null, and return ServerBusyError.

I'd avoid checking the error code alone, because Codex forwards MCP errors with their original codes. Matching both code and message would narrow the compatibility check to the known native response, although it still couldn't distinguish an MCP server returning exactly the same pair.

Related issues

#19366 contains VS Code extension logs showing the same -32001 response without error data. Our reproduction uses that response to demonstrate a separate problem: the Python SDK's overload-retry helper doesn't retry it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with sdk/python/src/openai_codex/errors.py and sdk/python/src/openai_codex/retry.py, then compare the response constructed in codex-rs/app-server-transport/src/transport/mod.rs. Run the provided Python reproduction from the repository root and verify that the native -32001 response with its exact message is retried and succeeds on the second call, while unrelated errors are not retried.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.