Files
2026-07-12 10:17:17 -04:00

4.8 KiB

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

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

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.

# 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:

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:

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:

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:

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=<your-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:

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