Skip to content

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

@eric-tramel

Description

@eric-tramel

Bug Description

When configuring a per-task auxiliary override (e.g. auxiliary.session_search) with an explicit base_url and model but leaving api_key empty, the auxiliary client fails to inherit the main model.api_key from config.yaml. Instead it falls through to the hardcoded "no-key-required" fallback, causing 401 auth errors.

This is a problem for users on custom/self-hosted endpoints who want to use a cheaper model for auxiliary tasks while sharing the same API gateway and credentials as their main model.

Steps to Reproduce

# ~/.hermes/config.yaml
model:
  default: azure/anthropic/claude-opus-4-6
  provider: custom
  base_url: https://my-gateway.example.com/v1
  api_key: sk-my-real-key

auxiliary:
  session_search:
    provider: custom
    model: 'some/fast-model'
    base_url: 'https://my-gateway.example.com/v1'
    api_key: ''    # empty — expect it to inherit from model.api_key
    timeout: 30
  1. Configure as above (same base_url as main provider, different model, empty api_key)
  2. Trigger a session_search with a keyword query
  3. Observe 401 errors in ~/.hermes/logs/agent.log

Expected Behavior

When auxiliary.{task}.api_key is empty and auxiliary.{task}.base_url matches (or is the same gateway as) model.base_url, the auxiliary client should inherit model.api_key from config.yaml.

Actual Behavior

resolve_provider_client() receives explicit_api_key=None (empty string stripped). At line 1405-1408 of agent/auxiliary_client.py, the fallback chain is:

custom_key = (
    (explicit_api_key or "").strip()       # → empty
    or os.getenv("OPENAI_API_KEY", "").strip()  # → often unset
    or "no-key-required"                   # ← wins
)

The main model.api_key from config.yaml is never consulted. The request goes out with Authorization: Bearer no-key-required and the gateway rejects it:

Authentication Error, LiteLLM Virtual Key expected.
Received=no-key-required, expected to start with 'sk-'

Affected Component

Auxiliary client (agent/auxiliary_client.py), specifically resolve_provider_client() custom provider path.

Proposed Fix

When provider=custom with an explicit base_url but no api_key, add model.api_key from config.yaml to the 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 in config
    or "no-key-required"
)

Where _read_main_api_key() reads config.get("model", {}).get("api_key", "").

This is especially relevant for the provider: auto path as well — when auto-detection routes auxiliary tasks to the main custom endpoint but the env var is not set, the same fallback failure occurs.

Workaround

Duplicate the API key explicitly in every auxiliary.{task}.api_key field that uses a custom base_url.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/configConfig system, migrations, profilescomp/agentCore agent loop, run_agent.py, prompt buildertype/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