Skip to content

[Bug] Provider auto-detection ignores model.provider in config.yaml — stale API keys override explicit custom endpoint #4172

@SHL0MS

Description

@SHL0MS

Problem

Users who configure a custom endpoint (e.g., local Ollama on DGX Spark) via `hermes setup` find that Hermes still routes through OpenRouter and burns paid API tokens. `/provider` shows `ollama/qwen3.5 via OpenRouter` despite the user explicitly selecting their local endpoint and having no OpenRouter configuration.

This affects both the main chat model and all auxiliary tasks (compression, vision, memory flush, etc.).

Three root causes

1. `resolve_runtime_provider()` has no handler for `provider == "custom"` (primary bug)

`runtime_provider.py:304-428` handles `nous`, `openai-codex`, `copilot-acp`, `anthropic`, and API-key providers. When `resolve_provider()` returns `"custom"`, there is no matching branch. It falls through to `_resolve_openrouter_runtime()` at line 422, which returns an OpenRouter runtime.

Additionally, `_get_named_custom_provider()` at line 133 explicitly returns `None` when `requested_provider == "custom"` — it's treated as a generic label, not a resolvable provider.

This is why the user sees "endpoint is localhost but provider is OpenRouter" — the base_url resolves correctly, but the provider label comes from the OpenRouter fallback.

2. `resolve_provider()` ignores `model.provider` in config.yaml

`auth.py:667` runs an auto-detection chain:

```
Priority (broken):

  1. Explicit CLI args
  2. auth.json active_provider (OAuth)
  3. OPENAI_API_KEY or OPENROUTER_API_KEY in env → "openrouter"
  4. Provider-specific API keys → that provider
  5. Fallback: raise error

Missing: model.provider from config.yaml is NEVER checked.
```

`hermes setup` correctly writes `model.provider: "custom"` to config.yaml, but `resolve_provider()` doesn't read it. A stale `OPENROUTER_API_KEY` in `.env` from a previous configuration wins.

3. `_model_flow_custom()` doesn't clear stale provider keys

`main.py:1292` calls `deactivate_provider()` which clears `active_provider` in auth.json, but does NOT clear `OPENROUTER_API_KEY` from `.env`. The stale key survives and wins the auto-detect on next startup.

Reproduction

  1. Fresh install. Run `hermes setup`, select "Custom endpoint", enter `http://localhost:11434/v1\` (Ollama)
  2. Previously had `OPENROUTER_API_KEY` set (or it's still in `.env` from a prior config)
  3. Start `hermes`, type `/provider`
  4. Expected: `Current: qwen3.5:35b via Custom endpoint (localhost:11434)`
  5. Actual: `Current: ollama/qwen3.5 via OpenRouter`

Even without stale keys, root cause #1 alone causes the bug — `provider == "custom"` always falls through to OpenRouter.

Impact

  • Severity: High — users are unknowingly billed for API calls they thought they avoided
  • Affected users: Anyone who switches from a cloud provider to a local endpoint
  • Workaround: None that fully works (clearing stale keys helps with root cause Support passing morph snapshot id #2-3 but not Terminal tool #1)

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/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