Skip to content

fix: prefer ~/.hermes/.env over stale os.environ when seeding credential pool (salvage #18256)#18755

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-3d89efe9
May 2, 2026
Merged

fix: prefer ~/.hermes/.env over stale os.environ when seeding credential pool (salvage #18256)#18755
teknium1 merged 3 commits into
mainfrom
hermes/hermes-3d89efe9

Conversation

@teknium1

@teknium1 teknium1 commented May 2, 2026

Copy link
Copy Markdown
Contributor

When OPENROUTER_API_KEY is set in both the parent shell (stale) and ~/.hermes/.env (fresh), _seed_from_env now prefers the .env value. Before this fix, any stale export from a parent process (Codex CLI, test scripts, previous install) silently overrode the key the user just wrote to .env, wrote the stale value to auth.json, and caused persistent 401 errors with no obvious cause.

Cherry-picked from @franksong2702's #18256 onto current main, plus 2 regression tests (precedence + fallback).

What changed

  • agent/credential_pool.py: _seed_from_env uses a local _get_env_prefer_dotenv() helper that reads ~/.hermes/.env first, then falls back to os.environ.
  • tests/agent/test_credential_pool.py: 2 regression tests added —
    • test_load_pool_prefers_dotenv_over_stale_os_environ covers the bug.
    • test_load_pool_falls_back_to_os_environ_when_dotenv_empty guards the Docker/K8s/systemd path where .env is absent and runtime env vars are the only source.

Why only in credential-pool seeding (not globally)

The .env file is the user's managed Hermes config; inside Hermes' own credential-pool cache seeding, it's authoritative. General env resolution (hermes_cli/env_loader.py) still honors 12-factor precedence (os.environ wins) for runtime-injected secrets — changing that would break Docker/K8s deployments that only set env vars. The narrow scope here only affects which value ends up written to auth.json, not general env resolution.

Validation

  • scripts/run_tests.sh tests/agent/test_credential_pool.py → 41 passed (39 existing + 2 new).
  • E2E: real HERMES_HOME, real .env file, stale shell export of OPENROUTER_API_KEY, seeded auth.json with the stale key. After load_pool("openrouter"): selected entry's access_token is the fresh .env value, and auth.json on disk is rewritten with the fresh key.

Closes #18254.

Frank Song and others added 3 commits May 2, 2026 01:57
When _seed_from_env() reads API keys to populate the credential pool, it
should treat ~/.hermes/.env as the authoritative source — not os.environ.
Stale env vars inherited from parent shell processes (Codex CLI, test
scripts, etc.) can shadow deliberate changes to the .env file, causing
auth.json to cache an outdated key that leads to silent 401 errors.

This is especially visible with OpenRouter: if a parent process exported
OPENROUTER_API_KEY=test-key-fresh and the user later updates .env with a
valid key, restarting Hermes still picks up the stale os.environ value,
writes it back to auth.json, and all API calls fail with 401.

Fixes #18254
…cedence

Covers PR #18256 fix for issue #18254 — when OPENROUTER_API_KEY is set in
BOTH os.environ (stale from parent shell) and ~/.hermes/.env (fresh),
_seed_from_env must prefer the .env value. Also guards the fallback case
where .env omits the key entirely (Docker/K8s/systemd deployments that
only inject via runtime env).
@teknium1 teknium1 merged commit 0a6865b into main May 2, 2026
9 of 10 checks passed
@teknium1 teknium1 deleted the hermes/hermes-3d89efe9 branch May 2, 2026 09:00
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround area/auth Authentication, OAuth, credential pools comp/agent Core agent loop, run_agent.py, prompt builder labels May 2, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Salvage of #18256. Closes #18254. Related to #15920 (same class of .env resolution bug in auth.py).

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Salvage of #18256. Closes #18254. Related to #15920 (same class of .env resolution bug in auth.py).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent loop, run_agent.py, prompt builder P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: auth.json credential cache ignores .env changes — stale key persists

2 participants