Skip to content

Bug: _try_openrouter() returns (None,None) when credential pool is exhausted, blocking env var fallback #23452

@tltanlintl

Description

@tltanlintl

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

  1. Have OpenRouter credential pool with at least one entry that gets rate-limited (429)
  2. All pool entries enter cooldown
  3. Run any auxiliary task (e.g. title generation after a chat response)
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/agentCore agent loop, run_agent.py, prompt builderprovider/openrouterOpenRouter aggregatortype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions