Skip to content

fix(cli): clear stale api_key/api_mode when switching away from custom provider - #49362

Closed
infinitycrew39 wants to merge 3 commits into
NousResearch:mainfrom
infinitycrew39:fix/model-flow-stale-api-key-after-provider-switch
Closed

fix(cli): clear stale api_key/api_mode when switching away from custom provider#49362
infinitycrew39 wants to merge 3 commits into
NousResearch:mainfrom
infinitycrew39:fix/model-flow-stale-api-key-after-provider-switch

Conversation

@infinitycrew39

Copy link
Copy Markdown
Contributor

Problem

When a user switches from a named custom provider (which writes model.api_key to config.yaml) to a built-in provider like Nous Portal, OpenRouter, or any API-key provider, the old api_key (and optionally api_mode) entry was left behind in config.yaml. This silently overrode the new provider's credential resolution chain (which reads from .env, not config.yaml), causing authentication failures or unintended credential leakage.

Reported by @Latipun in Discord.

Root cause

Three model-selection flows set model.provider but did not clear the stale api_key:

  • _model_flow_nous: _update_config_for_provider() correctly popped api_key from the on-disk file, but save_config(config) was called immediately after with the stale in-memory config dict (passed in by the caller, still carrying the old key), reinstating the old api_key.
  • _model_flow_openrouter: loaded fresh config via load_config() and updated provider/base_url/api_mode, but never popped api_key.
  • _model_flow_api_key_provider: same omission as OpenRouter.

Fix

  • _model_flow_nous: pop api_key and api_mode from model_cfg before config["model"] = model_cfg (so the stale values from the caller's dict are discarded before writing).
  • _model_flow_openrouter: add model.pop("api_key", None) after setting the provider fields.
  • _model_flow_api_key_provider: same as OpenRouter.

Tests

New tests/hermes_cli/test_provider_switch_clears_api_key.py with three test classes, one per affected flow. Each starts with a config.yaml that has model.api_key set (simulating a prior custom-provider session) and asserts it is absent after switching providers.

…m provider

Three model-selection flows updated config.yaml with a new provider but
did not remove model.api_key or model.api_mode left over from a previous
custom-provider setup:

• _model_flow_nous: _update_config_for_provider() correctly popped the key
  from the on-disk file, but save_config(config) was called immediately
  after with the stale in-memory config dict (passed in by the caller),
  reinstating the old api_key.  Fix: pop api_key and api_mode from
  model_cfg before assigning it back to config["model"].

• _model_flow_openrouter: loaded fresh config with load_config() and set
  provider/base_url/api_mode, but never popped api_key.

• _model_flow_api_key_provider: same omission as OpenRouter.

The stale api_key would silently override the new provider's credential
resolution chain (which reads from .env), causing authentication failures
or unintended credential leakage in config.yaml.
Covers the three flows that failed to clear stale model.api_key when the
user switches from a custom endpoint to a built-in provider:

• TestNousFlowClearsApiKey: mocks the Nous login/credential chain and
  passes a stale in-memory config dict; asserts save_config is called
  without api_key or api_mode in the model section.

• TestOpenRouterFlowClearsApiKey: starts with a real config.yaml that has
  model.api_key and verifies the on-disk file no longer contains it after
  switching to OpenRouter.

• TestApiKeyProviderFlowClearsApiKey: same, for a generic api-key provider
  (DeepSeek).
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels Jun 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #49360 (concurrent competing fix for the same stale-credential bug, filed ~90s earlier by a different author — it fixes only the _model_flow_nous Nous-picker path via a load_config() reload), #8571 (earlier open fix for stale api_key on custom-provider switch).

This PR fixes the same _model_flow_nous regression as #49360 but is broader in scope (also clears stale api_key in _model_flow_openrouter and _model_flow_api_key_provider) and uses a model_cfg.pop("api_key"/"api_mode") mechanism instead of a config reload. Not a duplicate — cross-linked for reviewer navigation.

The previous version accidentally included a duplicate block that used
hermes_cli.model_setup_flows.* as patch targets. Those symbols don't
exist as module-level attributes (they are imported lazily inside each
function body), so unittest.mock raised AttributeError.

Correct targets: hermes_cli.auth.*, hermes_cli.models.*, hermes_cli.main.*,
hermes_cli.config.* — the source modules where each symbol is defined.
@teknium1

Copy link
Copy Markdown
Contributor

Closing as superseded by #49380 (#49380), which covers the same bug class across all ~13 provider-switch sites via a shared helper. You correctly extended the original Nous fix to the three main CLI flows (openrouter, api_key_provider) and prompted the wider audit that surfaced the rest — credited in the merged PR. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants