Bug Description
When OPENROUTER_API_KEY (or other env-seeded credentials) is updated in ~/.hermes/.env, the cached credential in auth.json (credential_pool.openrouter) is not invalidated. Hermes continues using the old cached key, resulting in silent 401 errors.
Steps to Reproduce
- Set
OPENROUTER_API_KEY=test-key-fresh in ~/.hermes/.env and start Hermes
- Update
~/.hermes/.env with a valid key (e.g. sk-or-...b854)
- Restart Hermes gateway
- Try to use an OpenRouter model → HTTP 401 "Missing Authentication header"
- Check
auth.json → credential still has access_token: "test-key-fresh"
Root Cause
The credential pool seeds from env vars via _seed_from_env() in agent/credential_pool.py. It calls get_env_value() which checks os.environ FIRST, then falls back to ~/.hermes/.env:
def get_env_value(key: str) -> Optional[str]:
"""Get a value from ~/.hermes/.env or environment."""
if key in os.environ:
return os.environ[key] # <-- takes priority over .env!
env_vars = load_env()
return env_vars.get(key)
If any parent process (e.g. Codex CLI, test script) has exported OPENROUTER_API_KEY=test-key-fresh, that stale value shadows the actual key in .env. The credential pool then caches this stale value in auth.json and never re-reads .env.
The fix should ensure that ~/.hermes/.env is the authoritative source for Hermes config, with os.environ only as a secondary fallback — not the override.
Expected Behavior
- Updating a key in
~/.hermes/.env + restarting Hermes should use the new key
auth.json cache should reflect the .env file content, not stale os.environ values from parent shell processes
Actual Behavior
auth.json retains a snapshot of whichever value was in os.environ at the time of first load
- Changing
.env does not update the cached credential
- Users get 401 errors with no indication that the cache is stale
Environment
- Hermes v0.12.0 (2026.4.30)
- macOS (local CLI)
- Tested with OpenRouter provider
Diagnostic Command
python3 -c "
import json
a = json.load(open(\"/Users/xuefusong/.hermes/auth.json\"))
c = a[\"credential_pool\"][\"openrouter\"][0]
print(f\"Cached key: {c[\"access_token\"]}\")
print(f\"Source: {c[\"source\"]}\")
print(f\"Status: {c.get(\"last_status\")}\")
import os
print(f\"Env var: {os.environ.get(\"OPENROUTER_API_KEY\", \"(not set)\")[:20]}...\")
" 2>&1
Bug Description
When
OPENROUTER_API_KEY(or other env-seeded credentials) is updated in~/.hermes/.env, the cached credential inauth.json(credential_pool.openrouter) is not invalidated. Hermes continues using the old cached key, resulting in silent 401 errors.Steps to Reproduce
OPENROUTER_API_KEY=test-key-freshin~/.hermes/.envand start Hermes~/.hermes/.envwith a valid key (e.g.sk-or-...b854)auth.json→ credential still hasaccess_token: "test-key-fresh"Root Cause
The credential pool seeds from env vars via
_seed_from_env()inagent/credential_pool.py. It callsget_env_value()which checksos.environFIRST, then falls back to~/.hermes/.env:If any parent process (e.g. Codex CLI, test script) has exported
OPENROUTER_API_KEY=test-key-fresh, that stale value shadows the actual key in.env. The credential pool then caches this stale value inauth.jsonand never re-reads.env.The fix should ensure that
~/.hermes/.envis the authoritative source for Hermes config, withos.environonly as a secondary fallback — not the override.Expected Behavior
~/.hermes/.env+ restarting Hermes should use the new keyauth.jsoncache should reflect the.envfile content, not staleos.environvalues from parent shell processesActual Behavior
auth.jsonretains a snapshot of whichever value was inos.environat the time of first load.envdoes not update the cached credentialEnvironment
Diagnostic Command