You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the providers: keyed schema for config.yaml, _normalize_provider_dict_entry (in hermes_cli/config.py) does not validate keys and does not validate that the base-URL value is actually a URL. Two problems compound:
The base-URL lookup checks ("api", "url", "base_url") in that order, accepting the first non-empty string. A non-URL literal in api: (e.g. the string openai-reverse-proxy) is silently accepted as the endpoint.
Only snake_case api_key is recognized. Hand-written camelCase (apiKey, baseUrl) is silently dropped.
The user sees mysterious APIConnectionError and Bearer no-key-required in outgoing requests with no hint that their config keys were misspelled or that api: must parse as a URL.
Not a duplicate of #6945 (user-defined providers unresolvable in /model picker — their api: value was a real URL so they hit a different downstream bug), #8919 (custom provider routing with model.api_base), or #9315 (custom_providers list priority conflict when two entries share a base_url).
Steps to Reproduce
Write this providers: entry in ~/.hermes/config.yaml:
Add nvidia to fallback_providers and trigger a call to it (e.g. let the primary provider fail).
Inspect the outgoing request via ~/.hermes/sessions/request_dump_*.json or the agent log.
Expected Behavior
Either the entry is rejected at load time with a clear error/warning naming the unknown keys (apiKey, baseUrl), or camelCase keys are accepted as aliases. In either case, api: openai-reverse-proxy should never be silently interpreted as a base URL because it does not parse as a URL (no scheme, no host).
Actual Behavior
base_url resolves to the literal string "openai-reverse-proxy".
HTTP calls go to URLs like openai-reverse-proxy/v1/models and fail with APIConnectionError: Invalid URL 'openai-reverse-proxy/v1/models': No scheme supplied.
Authorization header is Bearer no-key-required because neither apiKey nor api_key is found, and the runtime falls through to the local-server placeholder in _ensure_runtime_credentials.
No warning is logged at config load time.
Affected Component
Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
N/A (CLI only)
Operating System
macOS 26.4.1
Python Version
3.11.15
Hermes Version
v0.9.0 (2026.4.13)
Relevant Logs / Traceback
DEBUG agent.model_metadata: Failed to fetch model metadata from openai-reverse-proxy/models: Invalid URL 'openai-reverse-proxy/v1/models': No scheme supplied. Perhaps you meant https://openai-reverse-proxy/v1/models?
INFO agent.model_metadata: Could not detect context length for model 'nvidia/llama-3.1-nemotron-ultra-253b-v1' at openai-reverse-proxy/ — defaulting to 128,000 tokens (probe-down).
Unknown keys are neither read nor warned about. There is a separate api_mode field, so users reasonably assume api means "API protocol", not "base URL" — and when they misuse it, the config is accepted silently.
Proposed Fix (optional)
Validate providers.<name> entries against a known field set (analogous to _VALID_CUSTOM_PROVIDER_FIELDS) and logger.warning on unknown keys, specifically calling out common camelCase mistakes (apiKey, baseUrl, apiMode).
Validate that the value chosen for base_url from ("api", "url", "base_url") actually parses as a URL with a scheme and netloc (urllib.parse.urlparse(v).scheme and urlparse(v).netloc). Reject non-URL strings with a clear error at config load rather than silently accepting them. This keeps api: working for users who already supply a valid URL ([Bug]: User-defined providers from providers: config cannot be resolved via /model picker or --provider flag #6945) while catching typos and misused field names.
Optional: accept apiKey/baseUrl as deprecated aliases with a one-time logger.warning to ease migration from hand-written configs.
Bug Description
In the
providers:keyed schema forconfig.yaml,_normalize_provider_dict_entry(inhermes_cli/config.py) does not validate keys and does not validate that the base-URL value is actually a URL. Two problems compound:("api", "url", "base_url")in that order, accepting the first non-empty string. A non-URL literal inapi:(e.g. the stringopenai-reverse-proxy) is silently accepted as the endpoint.api_keyis recognized. Hand-written camelCase (apiKey,baseUrl) is silently dropped.The user sees mysterious
APIConnectionErrorandBearer no-key-requiredin outgoing requests with no hint that their config keys were misspelled or thatapi:must parse as a URL.Not a duplicate of #6945 (user-defined providers unresolvable in
/modelpicker — theirapi:value was a real URL so they hit a different downstream bug), #8919 (customprovider routing withmodel.api_base), or #9315 (custom_providerslist priority conflict when two entries share abase_url).Steps to Reproduce
providers:entry in~/.hermes/config.yaml:nvidiatofallback_providersand trigger a call to it (e.g. let the primary provider fail).~/.hermes/sessions/request_dump_*.jsonor the agent log.Expected Behavior
Either the entry is rejected at load time with a clear error/warning naming the unknown keys (
apiKey,baseUrl), or camelCase keys are accepted as aliases. In either case,api: openai-reverse-proxyshould never be silently interpreted as a base URL because it does not parse as a URL (no scheme, no host).Actual Behavior
base_urlresolves to the literal string"openai-reverse-proxy".openai-reverse-proxy/v1/modelsand fail withAPIConnectionError: Invalid URL 'openai-reverse-proxy/v1/models': No scheme supplied.Authorizationheader isBearer no-key-requiredbecause neitherapiKeynorapi_keyis found, and the runtime falls through to the local-server placeholder in_ensure_runtime_credentials.Affected Component
Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
N/A (CLI only)
Operating System
macOS 26.4.1
Python Version
3.11.15
Hermes Version
v0.9.0 (2026.4.13)
Relevant Logs / Traceback
Request dump excerpt (
~/.hermes/sessions/request_dump_*.json):{ "method": "POST", "url": "openai-chat/chat/completions", "headers": {"Authorization": "Bearer no-key-required", "Content-Type": "application/json"} }Root Cause Analysis (optional)
hermes_cli/config.py,_normalize_provider_dict_entry(~line 1580):Unknown keys are neither read nor warned about. There is a separate
api_modefield, so users reasonably assumeapimeans "API protocol", not "base URL" — and when they misuse it, the config is accepted silently.Proposed Fix (optional)
providers.<name>entries against a known field set (analogous to_VALID_CUSTOM_PROVIDER_FIELDS) andlogger.warningon unknown keys, specifically calling out common camelCase mistakes (apiKey,baseUrl,apiMode).base_urlfrom("api", "url", "base_url")actually parses as a URL with a scheme and netloc (urllib.parse.urlparse(v).scheme and urlparse(v).netloc). Reject non-URL strings with a clear error at config load rather than silently accepting them. This keepsapi:working for users who already supply a valid URL ([Bug]: User-defined providers from providers: config cannot be resolved via /model picker or --provider flag #6945) while catching typos and misused field names.apiKey/baseUrlas deprecated aliases with a one-timelogger.warningto ease migration from hand-written configs.Are you willing to submit a PR for this?