# NousResearch/hermes-agent · Shorten 401 credential blackout duration

## Open now

- Nothing open on GitHub.

These items address a problem where a transient 401 error causes a credential to be blacklisted for 24 hours, with proposed fixes to shorten that cooldown period. The concern is especially acute for single-key setups where one bad response locks out the only available credential.

## 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 #21264. Verify it addresses your case.
- **Recommended:** read PR #21264 (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-21264`. 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

- #5668: Transient 401 blacklists credential for 24 hours — breaks single-key setups

## Prior & competing PRs

- **#21264: fix(auth): shorten credential 401 cooldown to 5 minutes (salvage #19267)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `agent/credential_pool.py`, `tests/agent/test_credential_pool.py`
  - fix hunks:
  - `agent/credential_pool.py`

    ```diff
    @@ -68,8 +68,10 @@ def _load_config_safe() -> Optional[dict]:
     }
     
     # Cooldown before retrying an exhausted credential.
    -# 429 (rate-limited) and 402 (billing/quota) both cool down after 1 hour.
    +# Transient 401 auth failures cool down briefly so single-key setups can recover.
    +# 429 (rate-limited), 402 (billing/quota), and other failures cool down after 1 hour.
     # Provider-supplied reset_at timestamps override these defaults.
    +EXHAUSTED_TTL_401_SECONDS = 5 * 60           # 5 minutes
     EXHAUSTED_TTL_429_SECONDS = 60 * 60          # 1 hour
     EXHAUSTED_TTL_DEFAULT_SECONDS = 60 * 60      # 1 hour
     
    ```

    ```diff
    @@ -190,6 +192,8 @@ def _is_manual_source(source: str) -> bool:
     
     def _exhausted_ttl(error_code: Optional[int]) -> int:
         """Return cooldown seconds based on the HTTP status that caused exhaustion."""
    +    if error_code == 401:
    +        return EXHAUSTED_TTL_401_SECONDS
         if error_code == 429:
             return EXHAUSTED_TTL_429_SECONDS
         return EXHAUSTED_TTL_DEFAULT_SECONDS
    ```
  - `tests/agent/test_credential_pool.py`

    ```diff
    @@ -250,6 +250,42 @@ def test_exhausted_402_entry_resets_after_one_hour(tmp_path, monkeypatch):
         assert entry.last_status == "ok"
     
     
    +def test_exhausted_401_entry_resets_after_five_minutes(tmp_path, monkeypatch):
    +    """Transient auth failures should not strand single-key setups for an hour."""
    +    monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
    +    _write_auth_store(
    +        tmp_path,
    +        {
    +            "version": 1,
    +            "credential_pool": {
    +                "openrouter": [
    +                    {
    +                        "id": "cred-1",
    +                        "label": "primary",
    +                        "auth_type": "api_key",
    +                        "priority": 0,
    +                        "source": "manual",
    +                        "access_token": "***",
    +                        "base_url": "https://openrouter.ai/api/v1",
    +                        "last_status": "exhausted",
    +                        "last_status_at": time.time() - 310,
    +                        "last_error_code": 401,
    +                    }
    +                ]
    +            },
    +        },
    +    )
    +
    +    from agent.credential_pool import load_pool
    +
    +    pool = load_pool("openrouter")
    +    entry = pool.select()
    +
    +    assert entry is not None
    +    assert entry.id == "cred-1"
    +    assert entry.last_status == "ok"
    +
    +
     def test_explicit_reset_timestamp_overrides_default_429_ttl(tmp_path, monkeypatch):
         monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
         # Prevent auto-seeding from Codex CLI tokens on the host
    ```
- **#19267: fix(auth): shorten credential 401 cooldown**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`

## Fix status

- Merged to main in PR #21264 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-07`.
- Subsystem: `agent`
