fix: warn on provider/model mismatch, surface auth errors (#266) - #283
Conversation
Fixes #266 — WebUI silently ignores provider/model selection mismatch. The problem: selecting an OpenRouter (or Anthropic/OpenAI) model while Hermes is configured for a different provider (e.g. local Ollama) sends the request to the wrong endpoint, which returns a 401 Unauthorized error with no UI indication of why. Three-layer fix: 1. api/streaming.py — detect 401/auth errors explicitly Added is_auth_error detection covering '401', 'AuthenticationError', 'authentication', 'unauthorized', 'invalid api key', and the specific Ollama error string 'no cookie auth credentials'. Auth errors emit apperror with type='auth_mismatch' and a hint pointing to 'hermes model'. 2. static/ui.js — expose active_provider and warn on selection - populateModelDropdown() stores data.active_provider from /api/models as window._activeProvider (the field was already in the response but the frontend never used it) - New _checkProviderMismatch(modelId) helper: compares the selected model's slash-prefix (e.g. 'openai/' from 'openai/gpt-4o') against the active provider. Skips the check for 'openrouter' and 'custom' to avoid false positives on configs that legitimately route any model. 3. static/boot.js — warn on model dropdown change modelSelect.onchange calls _checkProviderMismatch() and shows a toast when the selected model looks incompatible with the configured provider. 4. static/messages.js — distinct UI label for auth errors apperror handler now distinguishes type='auth_mismatch' and shows 'Provider mismatch' as the error label instead of 'Error'. 5. static/i18n.js — provider_mismatch_warning and provider_mismatch_label keys added to all 5 locales (en, es, de, zh-Hans, zh-Hant). Tests: 21 new tests in tests/test_provider_mismatch.py covering all five change areas. 679/679 total pass (658 baseline + 21 new).
1. ui.js: _checkProviderMismatch passed [modelId, ap] as a single
array arg to t(). Since t(key, ...args) spreads, the function
received the array as m and undefined as p. Fixed to pass as
separate args: t('provider_mismatch_warning', modelId, ap).
2. messages.js: 'Provider mismatch' label was hardcoded instead of
using t('provider_mismatch_label'). Now uses the i18n key with
fallback for when t() isn't available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review: fix/provider-mismatch-warning (#266)Full end-to-end review: all 6 files read line-by-line, security audit, 21 new tests run, 2 bugs found and fixed. Fixes pushed (commit 94c6f94)BUG 1 — // Before (broken):
window.t('provider_mismatch_warning', [modelId, ap])
// t(key, ...args) spreads — function received m=[modelId,ap], p=undefined
// Warning showed: '"openai/gpt-4o,anthropic" may not work...' (array stringified)
// After (fixed):
window.t('provider_mismatch_warning', modelId, ap)
// Function correctly receives m=modelId, p=apBUG 2 — hardcoded label instead of i18n key (messages.js:238) // Before:
const label = isAuthMismatch ? 'Provider mismatch' : 'Error';
// ^ Always English, even when UI is set to Chinese/Spanish/German
// After:
const label = isAuthMismatch ? (typeof t==='function' ? t('provider_mismatch_label') : 'Provider mismatch') : 'Error';The Security audit: CLEAN
Code reviewstreaming.py (lines 463-487) — auth error classification: correct
ui.js (lines 75-99) —
boot.js (lines 212-216) — onchange handler: correct
i18n.js — all 5 locales covered: en, es, de, zh, zh-Hant all have both tests/test_provider_mismatch.py (266 lines, 21 tests) — comprehensive
One note (not blocking)The One pre-existing flaky test (NOT from this PR)
Summary
Looks good. Two real bugs caught and fixed. Ready for merge whenever. |
|
Agent review — APPROVED ✅ Full independent review completed after the other agent's two bug fixes. Commits reviewed: Security: CLEAN
Tests: 679/679 pass (658 baseline + 21 new). Zero regressions. QA harness: PASS (Phase 1 structural + Phase 2 browser API, 11/11) Browser feature test (port 8789):
Both agent-found bugs confirmed fixed:
Ready to merge. |
What this fixes
Closes #266 — WebUI silently fails with a 401 error when the user selects a model from a different provider than the one Hermes is configured for (e.g. selecting
openai/gpt-4owhile Hermes is configured for a local Ollama endpoint).The root cause:
hermes modelconfigures a provider inconfig.yaml, but the WebUI model dropdown lists all available models. Selecting one from the wrong provider sends the request through the wrong endpoint, returning a 401 with no UI feedback.Changes
api/streaming.py'401',AuthenticationError,'unauthorized','invalid api key','no cookie auth credentials'(the Ollama-specific string from the issue)apperrorwithtype='auth_mismatch'and a hint: "The selected model may not be supported by your configured provider. Runhermes modelin your terminal to switch providers, then restart the WebUI."static/ui.jspopulateModelDropdown()now storesdata.active_providerfrom/api/modelsaswindow._activeProvider(the field was already in the API response but the frontend never read it)_checkProviderMismatch(modelId)helper: compares the selected model's provider prefix against the active provider, returns a warning string if they differ. Skips the check foropenrouterandcustomto avoid false positivesstatic/boot.jsmodelSelect.onchangecalls_checkProviderMismatch()and surfaces the warning viashowToast()when the user picks an incompatible modelstatic/messages.jsapperrorhandler now shows"Provider mismatch"instead of"Error"fortype='auth_mismatch'eventsstatic/i18n.jsprovider_mismatch_warning(arrow function with model/provider interpolation) andprovider_mismatch_labeladded to all 5 locales: en, es, de, zh-Hans, zh-Hanttests/test_provider_mismatch.pyTest results
679 passed, 0 failed (658 baseline + 21 new)