# NousResearch/hermes-agent · HTTP 429 rate limit and Retry-After header handling

## Open now

- Nothing open on GitHub.

These items address a bug where HTTP 429 Too Many Requests responses were not properly handled, covering both honouring the Retry-After header and surfacing user-friendly rate limit messages.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #3809. Verify it addresses your case.
- **Recommended:** read PR #3809 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.50`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #2642: \[Bug\]: HTTP 429 Too Many Requests is not treated

## Prior & competing PRs

- **#3809: fix(agent): user-friendly 429 rate limit messages with Retry-After support**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `run_agent.py`
  - fix hunks:
  - `run_agent.py`

    ```diff
    @@ -7229,7 +7229,10 @@ def _stop_spinner():
                                 retry_count = 0
                                 continue
                             _final_summary = self._summarize_api_error(api_error)
    -                        self._vprint(f"{self.log_prefix}❌ Max retries ({max_retries}) exceeded. Giving up.", force=True)
    +                        if is_rate_limited:
    +                            self._vprint(f"{self.log_prefix}❌ Rate limit persisted after {max_retries} retries. Please try again later.", force=True)
    +                        else:
    +                            self._vprint(f"{self.log_prefix}❌ Max retries ({max_retries}) exceeded. Giving up.", force=True)
                             self._vprint(f"{self.log_prefix}   💀 Final error: {_final_summary}", force=True)
     
                             # Detect SSE stream-drop pattern (e.g. "Network
    ```

    ```diff
    @@ -7289,8 +7292,22 @@ def _stop_spinner():
                                 "error": _final_summary,
                             }
     
    -                    wait_time = min(2 ** retry_count, 60)  # Exponential backoff: 2s, 4s, 8s, 16s, 32s, 60s, 60s
    -                    self._emit_status(f"⏳ Retrying in {wait_time}s (attempt {retry_count}/{max_retries})...")
    +                    # For rate limits, respect the Retry-After header if present
    +                    _retry_after = None
    +                    if is_rate_limited:
    +                        _resp_headers = getattr(getattr(api_error, "response", None), "headers", None)
    +                        if _resp_headers and hasattr(_resp_headers, "get"):
    +                            _ra_raw = _resp_headers.get("retry-after") or _resp_headers.get("Retry-After")
    +                            if _ra_raw:
    +                                try:
    +                                    _retry_after = min(int(_ra_raw), 120)  # Cap at 2 minutes
    +                                except (TypeError, ValueError):
    +                                    pass
    +                    wait_time = _retry_after if _retry_after else min(2 ** retry_count, 60)
    +                    if is_rate_limited:
    +                        self._emit_status(f"⏱️ Rate limit reached. Waiting {wait_time}s before retry (attempt {retry_count + 1}/{max_retries})...")
    +                    else:
    +                        self._emit_status(f"⏳ Retrying in {wait_time}s (attempt {retry_count}/{max_retries})...")
                         logger.warning(
                             "Retrying API call in %ss (attempt %s/%s) %s error=%s",
                             wait_time,
    ```
- **#2643: fix: honour Retry-After header on HTTP 429 responses - Fixes #2642**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`
- **#2724: fix(api): honour Retry-After header on HTTP 429 Too Many Requests**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `ed6427e0a77215bd4220c15951f28908c745e4ba`, `#3809`

## Fix status

- Merged to main in PR #3809 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-03-29`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `agent`
