# NousResearch/hermes-agent · Model switching routing and provider resolution bugs

## Open now

- Nothing open on GitHub.

These issues and fixes address problems where model-switching commands incorrectly route to native providers instead of configured or custom providers, leak environment/process state across sessions, and fail to honor launch-time or runtime provider overrides.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #16897. Verify it addresses your case.
- **Recommended:** read PR #16897 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-16897`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member 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.

## Closed / duplicate issues

- #16857: TUI: /new after /model switch to custom provider model fails with 'no API key' when model name exists in native provider catalog

## Prior & competing PRs

- **#16897: fix(tui): \`/model\` writes HERMES_TUI_PROVIDER unconditionally (#16857)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `tests/test_tui_gateway_server.py`, `tui_gateway/server.py`
  - fix hunks:
  - `tests/test_tui_gateway_server.py`

    ```diff
    @@ -1171,6 +1171,61 @@ def switch_model(self, **_kwargs):
         assert os.environ["HERMES_INFERENCE_PROVIDER"] == "anthropic"
     
     
    +def test_config_set_model_syncs_tui_provider_unconditionally(monkeypatch):
    +    """Regression for #16857: /model must set HERMES_TUI_PROVIDER even when
    +    it wasn't pre-set on launch, so a later /new (which re-runs
    +    _resolve_startup_runtime) honours the user's explicit provider choice
    +    instead of falling through to static-catalog detection and picking a
    +    coincidentally-matching native provider.
    +    """
    +
    +    class _Agent:
    +        provider = "openrouter"
    +        model = "old/model"
    +        base_url = ""
    +        api_key = "sk-or"
    +
    +        def switch_model(self, **_kwargs):
    +            return None
    +
    +    result = types.SimpleNamespace(
    +        success=True,
    +        new_model="deepseek-v4-pro",
    +        target_provider="custom:xuanji",
    +        api_key="[redacted:secret-assignment]",
    +        base_url="https://xuanji.example/v1",
    +        api_mode="chat_completions",
    +        warning_message="",
    +    )
    +
    +    server._sessions["sid"] = _session(agent=_Agent())
    +    monkeypatch.delenv("HERMES_TUI_PROVIDER", raising=False)
    +    monkeypatch.delenv("HERMES_INFERENCE_PROVIDER", raising=False)
    +    monkeypatch.setattr(
    +        "hermes_cli.model_switch.switch_model", lambda **_kwargs: result
    +    )
    +    monkeypatch.setattr(server, "_restart_slash_worker", lambda session: None)
    +    monkeypatch.setattr(server, "_emit", lambda *args, **kwargs: None)
    +
    +    server.handle_request(
    +        {
    +            "id": "1",
    +            "method": "config.set",
    +            "params": {
    +                "session_id": "sid",
    +                "key": "model",
    +                "value": "deepseek-v4-pro --provider custom:xuanji",
    +            },
    +        }
    +    )
    +
    +    # Both env vars must reflect the user's choice. HERMES_TUI_PROVIDER is
    +    # the canonical explicit-this-process carrier consumed by
    +    # _resolve_startup_runtime() on /new.
    +    assert os.environ["HERMES_TUI_PROVIDER"] == "custom:xuanji"
    +    assert os.environ["HERMES_INFERENCE_PROVIDER"] == "custom:xuanji"
    +
    +
     def test_config_set_model_syncs_tui_provider_env(monkeypatch):
         class Agent:
             model = "gpt-5.3-codex"
    ```
  - `tui_gateway/server.py`

    ```diff
    @@ -843,14 +843,19 @@ def _apply_model_switch(sid: str, session: dict, raw_input: str) -> dict:
     
         os.environ["HERMES_MODEL"] = result.new_model
         os.environ["HERMES_INFERENCE_MODEL"] = result.new_model
    -    # Keep the process-level provider env var in sync with the user's explicit
    -    # choice so any ambient re-resolution (credential pool refresh, compressor
    -    # rebuild, aux clients) resolves to the new provider instead of the
    -    # original one persisted in config or env.
    +    # Keep the process-level provider env vars in sync with the user's
    +    # explicit choice so any ambient re-resolution (credential pool refresh,
    +    # compressor rebuild, aux clients) and startup re-resolution on /new
    +    # both pick up the new provider instead of the original one persisted
    +    # in config or env.
    +    #
    +    # HERMES_TUI_PROVIDER is the canonical "explicit-this-process" carrier
    +    # consumed by _resolve_startup_runtime() — set it unconditionally on
    +    # /model so /new can't fall through to static-catalog detection and
    +    # pick a coincidentally-matching native provider (fixes #16857).
         if result.target_provider:
             os.environ["HERMES_INFERENCE_PROVIDER"] = result.target_provider
    -        if os.environ.get("HERMES_TUI_PROVIDER"):
    -            os.environ["HERMES_TUI_PROVIDER"] = result.target_provider
    +        os.environ["HERMES_TUI_PROVIDER"] = result.target_provider
         if persist_global:
             _persist_model_switch(result)
         return {"value": result.new_model, "warning": result.warning_message or ""}
    ```
- **#16873: fix(gateway): honour HERMES_INFERENCE_PROVIDER in _resolve_startup_runtime (#16857)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#16897`, `#16857`, `#15755`

## Fix status

- Merged to main in PR #16897 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-28`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
