Skip to content

[Bug]: retry_request discards HTTP 429 rate-limit responses on retry exhaustion (raising bare RuntimeError) and crashes on standard HTTP-date Retry-After headers #718

Description

@Preciousuche

Duplicate check

  • I searched the existing open and closed issues and this is not a duplicate.

AgentOS version or commit

2026.9.1

Area

Channel integration

Reproduction steps

Define a mock endpoint or handler that returns HTTP 429 with Retry-After:
python
import asyncio, httpx
from agentos.channels._util import retry_request
async def mock_endpoint(*args, **kwargs):
return httpx.Response(429, headers={"Retry-After": "0.1"}, json={"error": "rate_limited"})
async def main():
resp = await retry_request(mock_endpoint, max_retries=1, base_delay=0.1)
asyncio.run(main())
Observe the exception raised when retries are exhausted.
Alternatively, supply an RFC 7231 / RFC 9110 compliant HTTP-date header:
python
headers = {"Retry-After": "Wed, 21 Oct 2026 07:28:00 GMT"}

Expected behavior

When retries are exhausted on a 429 response, retry_request should return the httpx.Response object (matching how 5xx statuses behave) so callers (Slack, Discord, Webhook delivery) can access the status code, response headers, and downstream error body.
Retry-After should safely parse both integer/float seconds and HTTP-date strings without throwing an unhandled ValueError.

Actual behavior

On the final retry attempt (attempt == max_retries), the 429 branch executes await asyncio.sleep(retry_after); continue without checking attempt < max_retries. The loop terminates and falls through to raise last_exc or RuntimeError("retry_request exhausted"). Because last_exc is None, a bare RuntimeError is raised and the response is permanently discarded.
If a server responds with an HTTP-date format in Retry-After, float(resp.headers.get("Retry-After")) raises an unhandled ValueError: could not convert string to float: 'Wed, 21 Oct 2026 07:28:00 GMT', crashing immediately on attempt 0.

Environment

Windows 10 / Linux / macOS, Python 3.12+ (tested on Python 3.14.3 with uv)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already existstype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions