Description
When the OpenRouter credential pool has entries but all of them are exhausted/rate-limited (e.g. HTTP 429), _try_openrouter() returns (None, None) without falling back to the OPENROUTER_API_KEY environment variable, even when that env var is valid and usable.
This causes auxiliary tasks (title_generation, compression, session_search, vision, etc.) to fail with:
No LLM provider configured for task=<task> provider=auto
Meanwhile the main agent continues to work fine because it uses a different code path.
Root Cause
In agent/auxiliary_client.py, _try_openrouter():
def _try_openrouter(explicit_api_key=None):
pool_present, entry = _select_pool_entry("openrouter")
if pool_present: # True — pool has entries, all exhausted
or_key = explicit_api_key or _pool_runtime_api_key(entry) # entry=None → ""
if not or_key:
return None, None # ← returns early, never reaches env var
# This code is unreachable when pool_present=True but all entries exhausted:
or_key = explicit_api_key or os.getenv("OPENROUTER_API_KEY")
The _select_pool_entry() helper returns (True, None) when the pool exists but all entries are in cooldown (pool.select() returns None). Since pool_present=True, the function bails out without ever checking os.getenv("OPENROUTER_API_KEY").
Steps to Reproduce
- Have OpenRouter credential pool with at least one entry that gets rate-limited (429)
- All pool entries enter cooldown
- Run any auxiliary task (e.g. title generation after a chat response)
- Observe the error: "No LLM provider configured for task=title_generation provider=auto"
Expected Behavior
When the credential pool exists but has no usable entries, _try_openrouter() should fall back to the OPENROUTER_API_KEY environment variable, which is the same behavior as when no pool is configured at all.
Suggested Fix
In _try_openrouter(), when pool_present=True but no usable entry is found, fall through to the env var check instead of returning early:
def _try_openrouter(explicit_api_key=None):
pool_present, entry = _select_pool_entry("openrouter")
if pool_present:
or_key = explicit_api_key or _pool_runtime_api_key(entry)
if or_key:
base_url = _pool_runtime_base_url(entry, OPENROUTER_BASE_URL) or OPENROUTER_BASE_URL
return OpenAI(api_key=or_key, base_url=base_url,
default_headers=build_or_headers()), _OPENROUTER_MODEL
# Fall through to env var fallback when pool entries are exhausted
logger.info("OpenRouter pool exhausted, falling back to OPENROUTER_API_KEY env var")
or_key = explicit_api_key or os.getenv("OPENROUTER_API_KEY")
...
Workaround (no source changes)
hermes auth reset openrouter
This clears the cooldown state on all pool entries.
Environment
- Hermes Agent version: latest (main)
- File:
agent/auxiliary_client.py function _try_openrouter()
- Provider: OpenRouter
Description
When the OpenRouter credential pool has entries but all of them are exhausted/rate-limited (e.g. HTTP 429),
_try_openrouter()returns(None, None)without falling back to theOPENROUTER_API_KEYenvironment variable, even when that env var is valid and usable.This causes auxiliary tasks (title_generation, compression, session_search, vision, etc.) to fail with:
Meanwhile the main agent continues to work fine because it uses a different code path.
Root Cause
In
agent/auxiliary_client.py,_try_openrouter():The
_select_pool_entry()helper returns(True, None)when the pool exists but all entries are in cooldown (pool.select()returnsNone). Sincepool_present=True, the function bails out without ever checkingos.getenv("OPENROUTER_API_KEY").Steps to Reproduce
Expected Behavior
When the credential pool exists but has no usable entries,
_try_openrouter()should fall back to theOPENROUTER_API_KEYenvironment variable, which is the same behavior as when no pool is configured at all.Suggested Fix
In
_try_openrouter(), whenpool_present=Truebut no usable entry is found, fall through to the env var check instead of returning early:Workaround (no source changes)
This clears the cooldown state on all pool entries.
Environment
agent/auxiliary_client.pyfunction_try_openrouter()