Skip to content

v11→v12 config migration converts custom_providers → providers: dict, but no runtime code reads providers: #8776

@ericxyz86

Description

@ericxyz86

Summary

The v11 → v12 config migration in hermes_cli/config.py:1809-1865 rewrites the legacy custom_providers: list into a new providers: dict, then deletes the list. But the runtime resolver and the fallback-activation resolver both read from custom_providers: (the list), so after the migration runs, named custom endpoints silently stop resolving at runtime.

Repro

Starting config (pre-migration, _config_version unset):

model:
  default: openai/gpt-5.4
  provider: openrouter
custom_providers:
- name: openai-direct
  base_url: https://api.openai.com/v1
  api_mode: codex_responses
fallback_providers:
- provider: openai-direct
  model: gpt-5-mini

Trigger migration (hermes setup, or any path that reaches migrate_config). The file ends up as:

model:
  default: openai/gpt-5.4
  provider: openrouter
providers:
  openai-direct:
    api: https://api.openai.com/v1
    name: openai-direct
    transport: codex_responses
fallback_providers:
- provider: openai-direct
  model: gpt-5-mini
_config_version: 16

Now when fallback activates:

hermes_cli.auth.AuthError: Unknown provider 'openai-direct'.

Because:

  • hermes_cli/runtime_provider.py:259 _get_named_custom_provider reads config.get(\"custom_providers\") and requires it to be a list — the migration just deleted it.
  • agent/auxiliary_client.py:1428-1429 (fallback activation path, reached via resolve_provider_client) calls the same _get_named_custom_provider.

Nothing in the codebase reads config[\"providers\"] at runtime. Grep confirms:

$ grep -rn 'config.get(\"providers\"\|config\[\"providers\"\]' hermes_cli/ agent/ run_agent.py
hermes_cli/config.py:1814   # migration target (write-only)
hermes_cli/config.py:1862   # migration target (write-only)

(The providers key in auth.py and model_switch.py refers to the unrelated auth_store[\"providers\"] persistence, not config.yaml.)

Impact

Any user who had custom_providers: and then upgrades past v11 loses runtime resolution for those endpoints. Fallback chains referencing the custom provider name throw AuthError silently in logs; smart-routing / auxiliary calls fall through to defaults. hermes doctor reports the config as healthy.

Suggested fix

One of the following:

  1. Update runtime resolvers to read from providers: dict in addition to (or instead of) custom_providers: list. _get_named_custom_provider and the fallback path in auxiliary_client.py:1420-1450 both need the change. Schema mapping:
    • providers[key].apibase_url
    • providers[key].transportapi_mode
    • providers[key].default_modelmodel
    • providers[key].api_keyapi_key
  2. Make the migration lossless — keep writing custom_providers: (list) alongside providers: (dict) until runtime code is updated, then cut over in a single release.
  3. Drop the migration for now if providers: dict was aspirational and isn't being wired up this cycle.

Happy to submit option (1) as a PR if that's the preferred direction.

Environment

  • Hermes 1cec910b (main, 2026-04-13)
  • Python 3.11, macOS arm64
  • _config_version: 16

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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