hermes - 💡(How to fix) Fix Bug: auth.json active_provider silently overrides model.provider in config.yaml at runtime

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Option C — Warn on mismatch:

Root Cause

File: hermes_cli/runtime_provider.pyresolve_runtime_provider()

When requested=None (no explicit provider arg), resolve_requested_provider() correctly reads model.provider from config.yaml. However, the credential pool resolution path (load_pool(provider)_normalize_pool_priorities()) uses active_provider from auth.json as the primary entry. If active_provider is set to an OAuth provider (e.g. openai-codex) that provider wins — the model.provider in config is never used at runtime.

Proof from live auth.json:

{
  "active_provider": "openai-codex",    ← stale from past The 'hermes login' command has been removed.
Use 'hermes auth' to manage credentials,
'hermes model' to select a provider, or 'hermes setup' for full setup.
  "providers": { "openai-codex": { ... } },
  "credential_pool": {
    "openai-codex": [{ "last_status": "exhausted", "last_error_code": 429 }]
  }
}

The active_provider slot survives across sessions, restarts, and config changes. Even after the Codex token is exhausted (429 usage_limit_reached), it is still selected first because the credential pool uses active_provider as the tiebreaker before checking exhaustion state.

Confounding factor: The config.yaml also had model: as a YAML list instead of a dict:

model:
- provider: opencode-go    ← WRONG: YAML list, not dict — silently ignored
  default: deepseek-v4-pro

This made _get_model_config() return {} (empty), but fixing only the YAML structure is insufficient — active_provider still overrides model.provider even when the YAML is correct.


Fix Action

Fix / Workaround

  • Any user who ever ran hermes login --provider openai-codex (or any OAuth provider) is permanently stuck on that provider, regardless of what they set in config.yaml.
  • The workaround requires manually editing auth.json to change active_provider.
  • No CLI command (e.g. hermes config set model.provider minimax) can override this because active_provider is read from the JSON auth store, not the config.

Code Example

{
  "active_provider": "openai-codex",    ← stale from past The 'hermes login' command has been removed.
Use 'hermes auth' to manage credentials,
'hermes model' to select a provider, or 'hermes setup' for full setup.
  "providers": { "openai-codex": { ... } },
  "credential_pool": {
    "openai-codex": [{ "last_status": "exhausted", "last_error_code": 429 }]
  }
}

---

model:
- provider: opencode-go    ← WRONG: YAML list, not dict — silently ignored
  default: deepseek-v4-pro

---

hermes provider set-active minimax

---

model:
     provider: minimax
     default: MiniMax-M2.7
RAW_BUFFERClick to expand / collapse

Bug Description

When: Every session start — /new, /reset, gateway restart, any new AIAgent instantiation.

Expected: Setting model.provider and model.default in config.yaml should determine which model runs by default.

Actual: The active_provider field in auth.json (set automatically by hermes login for OAuth providers like openai-codex) silently overrides config.yaml for runtime provider resolution. A user who explicitly configures model.provider: minimax in config.yaml still gets openai-codex (or whatever active_provider is set to) because credential pool resolution defers to active_provider before consulting the config.


Root Cause

File: hermes_cli/runtime_provider.pyresolve_runtime_provider()

When requested=None (no explicit provider arg), resolve_requested_provider() correctly reads model.provider from config.yaml. However, the credential pool resolution path (load_pool(provider)_normalize_pool_priorities()) uses active_provider from auth.json as the primary entry. If active_provider is set to an OAuth provider (e.g. openai-codex) that provider wins — the model.provider in config is never used at runtime.

Proof from live auth.json:

{
  "active_provider": "openai-codex",    ← stale from past The 'hermes login' command has been removed.
Use 'hermes auth' to manage credentials,
'hermes model' to select a provider, or 'hermes setup' for full setup.
  "providers": { "openai-codex": { ... } },
  "credential_pool": {
    "openai-codex": [{ "last_status": "exhausted", "last_error_code": 429 }]
  }
}

The active_provider slot survives across sessions, restarts, and config changes. Even after the Codex token is exhausted (429 usage_limit_reached), it is still selected first because the credential pool uses active_provider as the tiebreaker before checking exhaustion state.

Confounding factor: The config.yaml also had model: as a YAML list instead of a dict:

model:
- provider: opencode-go    ← WRONG: YAML list, not dict — silently ignored
  default: deepseek-v4-pro

This made _get_model_config() return {} (empty), but fixing only the YAML structure is insufficient — active_provider still overrides model.provider even when the YAML is correct.


Impact

  • Any user who ever ran hermes login --provider openai-codex (or any OAuth provider) is permanently stuck on that provider, regardless of what they set in config.yaml.
  • The workaround requires manually editing auth.json to change active_provider.
  • No CLI command (e.g. hermes config set model.provider minimax) can override this because active_provider is read from the JSON auth store, not the config.

Suggested Fix (Design)

Option A — Make config.yaml the authoritative source for provider selection:

model.provider in config.yaml should always take precedence over auth.json.active_provider for inference. The active_provider field should only control OAuth login flow, not runtime model routing. The fix would be in _normalize_pool_priorities() or the load_pool()pool.select() path: do not use active_provider as a defaulting mechanism for inference provider selection.

Option B — Add a CLI escape hatch:

hermes config set model.provider <name> should automatically clear or override active_provider in auth.json, making the config the single source of truth. Alternatively, add a new CLI command:

hermes provider set-active minimax

that is separate from OAuth login flow.

Option C — Warn on mismatch:

Add a validation step at startup that logs a warning when active_provider in auth.json conflicts with model.provider in config.yaml, so the mismatch is visible before it causes unexpected behavior.


Reproduction Steps

  1. Run hermes login --provider openai-codex (or any OAuth provider)
  2. Set in config.yaml:
    model:
      provider: minimax
      default: MiniMax-M2.7
  3. Run hermes or send a message to the gateway
  4. Observe: openai-codex is used, not minimax

Environment

  • Hermes Agent: v0.14.0 (2026.5.16)
  • Python: 3.11.14
  • Install method: pip / git
  • Relevant config: ~/.hermes/config.yaml, ~/.hermes/auth.json
  • Affected code path: hermes_cli/runtime_provider.pyresolve_runtime_provider()load_pool()_normalize_pool_priorities()

Labels

type/bug, area/auth, area/config, comp/cli, provider/all

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING