NousResearch/hermes-agent

explicit_api_key not forwarded to _try_openrouter

Cluster 18338. Data as of

Nothing open on GitHub. Fix merged in #18768 on 2 May 2026. Check it covers your case.

The short answer

0open issues
0open PRs
4items grouped
Known?
Yes. 4 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #18768. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #18768. That is separate from the fix event.
Still open
Nothing open on GitHub.

A bug where resolve_provider_client fails to pass explicit_api_key when calling _try_openrouter, and multiple fix attempts addressing this propagation gap.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #18768 fix(auxiliary): propagate explicit_api_key to _try_openrouter (salvage #18341)

    merged to main

    files: agent/auxiliary_client.py, tests/agent/test_auxiliary_client.py

    Show the diff
    • agent/auxiliary_client.py
      @@ -1149,10 +1149,10 @@ def _resolve_api_key_provider() -> Tuple[Optional[OpenAI], Optional[str]]:
       
       
       
      -def _try_openrouter() -> Tuple[Optional[OpenAI], Optional[str]]:
      +def _try_openrouter(explicit_api_key: str = None) -> Tuple[Optional[OpenAI], Optional[str]]:
           pool_present, entry = _select_pool_entry("openrouter")
           if pool_present:
      -        or_key = _pool_runtime_api_key(entry)
      +        or_key = explicit_api_key or _pool_runtime_api_key(entry)
               if not or_key:
                   return None, None
               base_url = _pool_runtime_base_url(entry, OPENROUTER_BASE_URL) or OPENROUTER_BASE_URL
      @@ -1160,7 +1160,7 @@ def _try_openrouter() -> Tuple[Optional[OpenAI], Optional[str]]:
               return OpenAI(api_key=[redacted:secret-assignment] base_url=base_url,
                              default_headers=_OR_HEADERS), _OPENROUTER_MODEL
       
      -    or_key = os.getenv("OPENROUTER_API_KEY")
      +    or_key = explicit_api_key or os.getenv("OPENROUTER_API_KEY")
           if not or_key:
               return None, None
           logger.debug("Auxiliary client: OpenRouter")
      @@ -2053,9 +2053,9 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "",
               return (_to_async_client(client, final_model, is_vision=is_vision) if async_mode
                       else (client, final_model))
       
      -    # ── OpenRouter ───────────────────────────────────────────────────
      +    # ── OpenRouter ───────────────────────────────────────────
           if provider == "openrouter":
      -        client, default = _try_openrouter()
      +        client, default = _try_openrouter(explicit_api_key=[redacted:secret-assignment]
               if client is None:
                   logger.warning(
                       "resolve_provider_client: openrouter requested but %s",
    • tests/agent/test_auxiliary_client.py
      @@ -1818,3 +1818,78 @@ def test_none_tools_unchanged(self):
                   provider="openai", model="gpt-4o", messages=[], tools=None,
               )
               assert "tools" not in kwargs
      +
      +
      [redacted:email](autouse=True)
      +def _clean_env(monkeypatch):
      +    """Strip provider env vars so each test starts clean."""
      +    for key in (
      +        "OPENROUTER_API_KEY", "OPENAI_BASE_URL", "OPENAI_API_KEY",
      +    ):
      +        monkeypatch.delenv(key, raising=False)
      +
      +
      +class TestOpenRouterExplicitApiKey:
      +    """Test that explicit_api_key is correctly propagated to _try_openrouter()."""
      +
      +    def test_resolve_provider_client_passes_explicit_api_key_to_openrouter(
      +        self, monkeypatch
      +    ):
      +        """
      +        When resolve_provider_client() is called with explicit_api_key for OpenRouter,
      +        the explicit key should be passed to the OpenAI client instead of falling back
      +        to OPENROUTER_API_KEY env var.
      +        """
      +        # Set up env var as fallback (should NOT be used when explicit_api_key is provided)
      +        monkeypatch.setenv("OPENROUTER_API_KEY", "env-fallback-key")
      +
      +        # Mock OpenAI to capture the api_key used
      +        mock_openai = MagicMock()
      +        mock_openai.return_value = MagicMock(name="openrouter-client")
      +
      +        with patch("agent.auxiliary_client.OpenAI", mock_openai):
      +            client, model = resolve_provider_client(
      +                provider="openrouter",
      +                explicit_api_key="[redacted:secret-assignment]",
      +            )
      +
      +            # Verify a client was created
      +            assert client is not None
      +            # Verify the explicit key was used, not the env var fallback
      +            mock_openai.assert_called_once()
      +            call_kwargs = mock_openai.call_args[1]
      +            assert call_kwargs["api_key"] == "explicit-pool-key", (
      +                f"Expected explicit_api_key to be passed, got: {call_kwargs['api_key']}"
      +            )
      +            assert call_kwargs["api_key"] != "env-fallback-key", (
      +                "Should NOT fall back to OPENROUTER_API_KEY when explicit_api_key is provided"
      +            )
      +
      +    def test_resolve_provider_client_without_explicit_api_key_falls_back_to_env(
      +        self, monkeypatch
      +    ):
      +        """
      +        When resolve_provider_client() is called WITHOUT explicit_api_key for OpenRouter,
      +        it should fall back to OPENROUTER_API_KEY env var.
      +        """
      +        # Set up env var as fallback (should be used when explicit_api_key is NOT provided)
      +        monkeypatch.setenv("OPENROUTER_API_KEY", "env-fallback-key")
      +
      +        # Mock OpenAI to capture the api_key used
      +        mock_openai = MagicMock()
      +        mock_openai.return_value = MagicMock(name="openrouter-client")
      +
      +        with patch("agent.auxiliary_client.OpenAI", mock_openai):
      +            client, model = resolve_provider_client(
      +                provider="openrouter",
      +                explicit_api_key=None,
      +            )
      +
      +            # Verify a client was created
      +            assert client is not None
      +            # Verify the env var fallback was used
      +            mock_openai.assert_called_once()
      +            call_kwargs = mock_openai.call_args[1]
      +            assert call_kwargs["api_key"] == "env-fallback-key", (
      +                f"Expected env fallback key to be used when explicit_api_key is None, got: {call_kwargs['api_key']}"
      +            )

2 PRs closed without merging

From the close comments: merged via another change 1 · superseded by another PR 1

  • #18341 fix(auxiliary): propagate explicit_api_key to _try_openrouter() · merged via another change → #18768
  • #18618 fix(auxiliary): forward explicit_api_key to _try_openrouter for credential pool fallback · superseded by another PR → #18768, #18341
Closed issues · 1
  • Closed#18338[Bug]: resolve_provider_client ignores explicit_api_key when calling _try_openrouter()
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.50: the share of item 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.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Merged PRs
#18768
Closed issues
1
Attempts
2 PRs closed without merging
Fix link checked
2 May 2026

For your agent

The same facts, typed.