# NousResearch/hermes-agent · Codex 429 quota fallback handling fixes

## Open now

- Nothing open on GitHub.

These items address a bug where interactive CLI sessions fail to auto-fallback when Codex returns a 429 usage-limit error, including fixes to error classification and fallback chain index reset logic.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #27185. Verify it addresses your case.
- **Recommended:** read PR #27185 (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: `resolved` · deciding PR(s): `pr-27185`. 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.67`: 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

- #20465: \[Bug\] Interactive CLI session does not auto-fallback on Codex 429 'usage_limit_reached', while cron jobs with the same fallback chain do

## Prior & competing PRs

- **#27185: fix(agent): reset _fallback_index at turn start even when no fallback activated**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `run_agent.py`, `tests/run_agent/test_primary_runtime_restore.py`
  - fix hunks:
  - `run_agent.py`

    ```diff
    @@ -9156,6 +9156,14 @@ def _restore_primary_runtime(self) -> bool:
             ``gateway/run.py``), so this restoration IS needed there too.
             """
             if not self._fallback_activated:
    +            # Reset the chain index even when no fallback was activated this
    +            # turn.  Without this, a turn where _try_activate_fallback() was
    +            # called but returned False (chain exhausted or provider not
    +            # configured) leaves _fallback_index >= len(_fallback_chain) while
    +            # _fallback_activated stays False.  The next turn skips this block
    +            # entirely, stranding the index and silently blocking all future
    +            # fallback attempts for the session.  Fixes #20465.
    +            self._fallback_index = 0
                 return False
     
             if getattr(self, "_rate_limited_until", 0) > time.monotonic():
    ```
  - `tests/run_agent/test_primary_runtime_restore.py`

    ```diff
    @@ -123,6 +123,26 @@ def test_noop_when_not_fallback(self):
             assert agent._fallback_activated is False
             assert agent._restore_primary_runtime() is False
     
    +    def test_resets_index_when_fallback_not_activated(self):
    +        """Regression for #20465: failed activation leaves _fallback_index advanced
    +        with _fallback_activated=False; the next turn's restore must reset the index."""
    +        fbs = [{"provider": "custom", "model": "gpt-oss:20b",
    +                "base_url": "http://host.docker.internal:11434/v1", "api_key": "[redacted:secret-assignment]"}]
    +        agent = _make_agent(fallback_model=fbs)
    +
    +        # resolve_provider_client returns None → _try_activate_fallback returns False
    +        # but _fallback_index has already been incremented to 1
    +        with patch("agent.auxiliary_client.resolve_provider_client", return_value=(None, None)):
    +            assert agent._try_activate_fallback() is False
    +
    +        assert agent._fallback_activated is False
    +        assert agent._fallback_index == 1  # advanced past the only entry
    +
    +        # _restore_primary_runtime must reset the index so the next turn can retry
    +        result = agent._restore_primary_runtime()
    +        assert result is False  # still no-op (primary was never left)
    +        assert agent._fallback_index == 0  # chain available again
    +
         def test_restores_model_and_provider(self):
             agent = _make_agent(
                 fallback_model={"provider": "openrouter", "model": "anthropic/claude-sonnet-4"},
    ```
- **#20793: fix(agent): reset fallback chain index when activation fails in interactive sessions**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#27185`

## Fix status

- Merged to main in PR #27185 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-17`.
