# Provider Credentials & Session State Diagnosis Diagnosis flow for HTTP 401 errors and "No LLM provider configured" when the web UI has the agent source installed (agent dir shows `[ok]` in logs). ## Diagnosis Flow ### Step 1 — Check what provider the web UI is actually trying to use ```bash docker exec hermes-webui cat /home/hermeswebui/.hermes/config.yaml ``` The `model.provider` field is the provider the web UI will attempt to use. If this says `openrouter` but your API keys are for `deepseek`, that's the problem. ### Step 2 — Check that the .env has the right API key ```bash docker exec hermes-webui cat /home/hermeswebui/.hermes/.env ``` The env var name must match what the provider expects. Common mappings: | provider in config.yaml | env var | |---|---| | `deepseek` | `DEEPSEEK_API_KEY` | | `openrouter` | `OPENROUTER_API_KEY` | | `anthropic` | `ANTHROPIC_API_KEY` | | `openai` / `openai-api` | `OPENAI_API_KEY` | | `gemini` | `GOOGLE_API_KEY` or `GEMINI_API_KEY` | ### Step 3 — Check if the error is from a stale cached session The web UI caches session metadata (model, provider, workspace) on disk. When you switch providers, old sessions keep the old provider. ```bash # Find the most recent session file ls -t /home/hermeswebui/.hermes/webui/sessions/*.json 2>/dev/null | head -3 # Check its model/provider python3 -c " import json, glob for f in sorted(glob.glob('/home/hermeswebui/.hermes/webui/sessions/*.json')): with open(f) as fh: d = json.load(fh) print(f'{f}: model={d.get(\"model\")} provider={d.get(\"model_provider\")}') " ``` Expected: `model=deepseek-v4-flash provider=deepseek` (or whatever your current provider is). If the session shows a different provider, update it: ```bash python3 -c " import json, glob for f in glob.glob('/home/hermeswebui/.hermes/webui/sessions/*.json'): with open(f) as fh: d = json.load(fh) d['model'] = 'deepseek-v4-flash' # <-- replace with your model d['model_provider'] = 'deepseek' # <-- replace with your provider with open(f, 'w') as fh: json.dump(d, fh) " ``` ### Step 4 — Check settings.json The web UI stores `default_model_provider` in settings.json: ```bash python3 -c " import json with open('/home/hermeswebui/.hermes/webui/settings.json') as f: d = json.load(f) print('default_model_provider:', d.get('default_model_provider')) " ``` If this doesn't match your current provider: ```bash python3 -c " import json with open('/home/hermeswebui/.hermes/webui/settings.json') as f: d = json.load(f) d['default_model_provider'] = 'deepseek' # <-- replace with your provider with open('/home/hermeswebui/.hermes/webui/settings.json', 'w') as f: json.dump(d, f, indent=2) " ``` ### Step 5 — Verify provider resolution works Test what the web UI resolves at runtime: ```bash python3 -c " import os os.environ['HERMES_HOME'] = '/home/hermeswebui/.hermes' from api.config import get_config, resolve_model_provider, model_with_provider_context cfg = get_config() print('Config model section:', cfg.get('model', {})) # Simulate sending a message without specifying a model model_with_ctx = model_with_provider_context('') resolved = resolve_model_provider(model_with_ctx) print('Resolved (empty model):', resolved) # Simulate sending a message with your default model model_with_ctx = model_with_provider_context('deepseek-v4-flash') resolved = resolve_model_provider(model_with_ctx) print('Resolved (default model):', resolved) # Check runtime provider can find the API key from api.oauth import resolve_runtime_provider_with_anthropic_env_lock from hermes_cli.runtime_provider import resolve_runtime_provider _rt = resolve_runtime_provider_with_anthropic_env_lock( resolve_runtime_provider, requested=resolved[1], # the resolved provider name ) print('API key found:', bool(_rt.get('api_key'))) " ``` If the key is found and provider/base_url are correct, the config is right and the issue is in the session cache. ## Common Patterns ### Pattern: OpenRouter → DeepSeek switch After switching from OpenRouter to DeepSeek, three things need updating: 1. `config.yaml` — set `model.provider: deepseek`, `model.default: deepseek-v4-flash`, `model.base_url: https://api.deepseek.com/v1` 2. `.env` — set `DEEPSEEK_API_KEY=` 3. Session cache — update session JSON files to use `model_provider: deepseek` instead of `openrouter` 4. `settings.json` — update `default_model_provider` to `deepseek` ### Pattern: Copying host config to web UI The host's `~/.hermes/` is NOT mounted into the web UI container. The web UI uses whatever is in the bind-mounted data directory (e.g. `/home/ray/docker/hermes/data/`). To sync: ```bash cp ~/.hermes/config.yaml /path/to/webui/data/config.yaml grep -E '^(DEEPSEEK|OPENROUTER|ANTHROPIC|OPENAI|GEMINI|GOOGLE)_API_KEY' ~/.hermes/.env >> /path/to/webui/data/.env ```