Skip to content

fix(auxiliary): inherit model.api_key for custom endpoint when per-task key is empty (#9318) - #55911

Closed
Tranquil-Flow wants to merge 1 commit into
NousResearch:mainfrom
Tranquil-Flow:fix/9318-aux-api-key-inherit
Closed

fix(auxiliary): inherit model.api_key for custom endpoint when per-task key is empty (#9318)#55911
Tranquil-Flow wants to merge 1 commit into
NousResearch:mainfrom
Tranquil-Flow:fix/9318-aux-api-key-inherit

Conversation

@Tranquil-Flow

Copy link
Copy Markdown
Contributor

Problem

When an auxiliary task is configured with provider: custom, an explicit base_url, and an empty api_key, the resolve_provider_client() custom provider branch falls through to the hardcoded "no-key-required" placeholder without ever consulting model.api_key from config.yaml. This causes 401 auth errors for users on self-hosted gateways who share the same endpoint and credentials for both their main model and auxiliary tasks.

# ~/.hermes/config.yaml
model:
  provider: custom
  base_url: https://my-gateway.example.com/v1
  api_key: sk-real-key

auxiliary:
  session_search:
    provider: custom
    model: 'some/fast-model'
    base_url: 'https://my-gateway.example.com/v1'
    api_key: ''  # empty — should inherit from model.api_key

Root Cause

The custom_key fallback chain in resolve_provider_client() (the provider == "custom" branch with explicit_base_url) only checks:

  1. explicit_api_key → None (empty string stripped)
  2. os.getenv("OPENAI_API_KEY") → often unset or wrong key
  3. "no-key-required" ← wins, causing 401

The main model.api_key from config.yaml is never consulted, unlike the _resolve_auto() path which already reads _RUNTIME_MAIN_API_KEY.

Fix

Added _read_main_api_key() — mirrors the existing _read_main_model() / _read_main_provider() pattern:

  1. Checks _RUNTIME_MAIN_API_KEY (runtime override set by set_runtime_main())
  2. Falls back to config.yamlmodel.api_key
  3. Returns "" if neither is set

Inserted into the custom_key fallback chain before "no-key-required":

custom_key = (
    (explicit_api_key or "").strip()
    or os.getenv("OPENAI_API_KEY", "").strip()
    or _read_main_api_key()           # NEW: inherit from model.api_key
    or "no-key-required"              # local servers (Ollama, etc.)
)

This covers both the explicit provider: custom path and (transitively) the provider: auto path when auto-detection routes auxiliary tasks to the main custom endpoint.

Tests

Added TestCustomEndpointApiKeyInheritance with 4 tests:

Test Type Description
test_inherits_main_api_key_when_aux_key_empty RED→GREEN Empty aux key + config model.api_key → inherits config key
test_runtime_override_key_is_used RED→GREEN _RUNTIME_MAIN_API_KEY override takes precedence over config
test_explicit_api_key_takes_precedence Guard rail Explicit api_key param wins over config
test_local_server_falls_to_no_key_required Guard rail No key anywhere → "no-key-required" for local servers

RED phase verified: both RED→GREEN tests fail on upstream/main with AssertionError: 'no-key-required' != 'sk-...'.

Full suite: 278 passed, 0 failed in tests/agent/test_auxiliary_client.py.

Related PRs

This fix is complementary to both — it handles the case where a user intentionally uses provider: custom with a self-hosted gateway and expects credential inheritance from the main model config.

Closes #9318.


Auto-published by Moonsong via Path B automated pipeline.

…sk key is empty (NousResearch#9318)

When an auxiliary task is configured with provider=custom and an explicit
base_url but an empty api_key, the custom_key fallback chain in
resolve_provider_client() jumped straight to the no-key-required
placeholder without consulting model.api_key from config.yaml.  Users
on self-hosted gateways who share the same endpoint and credentials for
both the main model and auxiliary tasks got 401 auth errors.

Add _read_main_api_key() following the same pattern as _read_main_model()
and _read_main_provider(): checks _RUNTIME_MAIN_API_KEY (runtime override)
first, then config.yaml model.api_key.  Insert it into the fallback chain
before no-key-required so real credentials are used when available, while
local servers without auth still get the placeholder.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have labels Jun 30, 2026
teknium1 added a commit that referenced this pull request Jul 6, 2026
Follow-up to the #55911 salvage: inherit model.api_key only when the aux
base_url resolves to the same hostname as the main model's base_url
(runtime override or config). A misconfigured aux endpoint on a different
host keeps the fail-safe no-key-required placeholder instead of leaking
the main credential cross-host.
@teknium1

teknium1 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Merged via PR #59207 — your commit was cherry-picked onto current main with your authorship preserved in git log. One hardening addition on top: the inheritance is host-gated (main key only inherited when the aux base_url points at the same host as the main model's base_url), so a misconfigured aux endpoint can't leak the main credential cross-host. Clean fix with a solid test suite — thanks!

@teknium1 teknium1 closed this Jul 6, 2026
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
Follow-up to the NousResearch#55911 salvage: inherit model.api_key only when the aux
base_url resolves to the same hostname as the main model's base_url
(runtime override or config). A misconfigured aux endpoint on a different
host keeps the fail-safe no-key-required placeholder instead of leaking
the main credential cross-host.
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
Follow-up to the NousResearch#55911 salvage: inherit model.api_key only when the aux
base_url resolves to the same hostname as the main model's base_url
(runtime override or config). A misconfigured aux endpoint on a different
host keeps the fail-safe no-key-required placeholder instead of leaking
the main credential cross-host.
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
Follow-up to the NousResearch#55911 salvage: inherit model.api_key only when the aux
base_url resolves to the same hostname as the main model's base_url
(runtime override or config). A misconfigured aux endpoint on a different
host keeps the fail-safe no-key-required placeholder instead of leaking
the main credential cross-host.
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
Follow-up to the NousResearch#55911 salvage: inherit model.api_key only when the aux
base_url resolves to the same hostname as the main model's base_url
(runtime override or config). A misconfigured aux endpoint on a different
host keeps the fail-safe no-key-required placeholder instead of leaking
the main credential cross-host.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Auxiliary client falls back to "no-key-required" when per-task custom base_url is set but api_key is empty

3 participants