hermes - 💡(How to fix) Fix env_loader.load_hermes_dotenv resolves ~/.hermes/.env via HERMES_HOME, disagrees with hermes config show

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

  1. OR sanity-check that home_path / '.env' is the same file hermes config show reports, and warn / fall back to Path.home() / '.hermes' / '.env' if they diverge.

Root Cause

hermes_cli/env_loader.py (location confirmed via inspect.getsourcefile):

home_path = Path(hermes_home or os.getenv("HERMES_HOME", Path.home() / ".hermes"))
user_env = home_path / ".env"

When HERMES_HOME is set to a path that is NOT ~/.hermes (e.g. the install-tree %LOCALAPPDATA%\hermes), user_env resolves to a different file than the one hermes config show reports as the secrets path. The two should agree.

Fix Action

Fix / Workaround

Workaround (what we deployed)

Code Example

# website/docs/user-guide/features/api-server.md
Add to `~/.hermes/.env`:

  API_SERVER_ENABLED=true
  API_SERVER_KEY=...

---

home_path = Path(hermes_home or os.getenv("HERMES_HOME", Path.home() / ".hermes"))
user_env = home_path / ".env"

---

=== dotenv diagnostic - read-only ===
  Host: PRETTYANDPINK
  OS:   Microsoft Windows 11 Pro
  PS:   7.6.2
  hermes: C:\Users\anf07\AppData\Local\hermes\hermes-agent\venv\Scripts\hermes.exe
  python: C:\Users\anf07\AppData\Local\hermes\hermes-agent\venv\Scripts\python.exe

--- 1. Path.home() resolution ---
Path.home() = C:\Users\anf07
USERPROFILE = C:\Users\anf07
HOMEDRIVE   = C:
HOMEPATH    = \Users\anf07

--- 2. load_hermes_dotenv signature + location ---
found: C:\Users\anf07\AppData\Local\hermes\hermes-agent\hermes_cli\env_loader.py
signature: (*, hermes_home: 'str | os.PathLike | None' = None, project_env: 'str | os.PathLike | None' = None) -> 'list[Path]'

--- 3. Where does config.py read API_SERVER_ENABLED? ---
  …\gateway\config.py L1522:
    api_server_enabled = os.getenv("API_SERVER_ENABLED", "").lower() in {"true", "1", "yes"}
  …\gateway\run.py L560:
    load_hermes_dotenv(hermes_home=_hermes_home, project_env=Path(__file__).resolve().parents[1] / '.env')

--- 4. ~/.hermes/.env content + encoding ---
  size: 158 bytes
  BOM: no (clean)
  line endings: 1 CR, 3 LF (MIXED)
  text content:
    HERMES_WEB_DIST=C:\Users\anf07\AppData\Local\hermes\hermes-agent\hermes_cli\web_dist
    API_SERVER_ENABLED=true
    API_SERVER_KEY=YHNxKMmBbkDLGV8bcoHdYhzPenOz34BC

--- 5. Env var state, three scopes ---
  API_SERVER_ENABLED     Process=(unset)   User=(set, 4 chars)    Machine=(unset)
  API_SERVER_KEY         Process=(unset)   User=(set, 32 chars)   Machine=(unset)
  HERMES_HOME            Process=(set)     User=(set, 35 chars)   Machine=(unset)
  USERPROFILE            Process=(set)     User=(unset)           Machine=(unset)

--- 6. Simulate the load-order race ---
  Spawn a fresh Python process that imports gateway.config WITHOUT first loading .env,
  then prints whether API_SERVER_ENABLED is visible.
imported gateway.config OK
pre-import,  API_SERVER_ENABLED = None
post-import, API_SERVER_ENABLED = None
RAW_BUFFERClick to expand / collapse

What's happening

On a Windows install where the Hermes installer set HERMES_HOME at User scope to C:\Users\<user>\AppData\Local\hermes, the gateway's load_hermes_dotenv() reads %LOCALAPPDATA%\hermes\.env (the install-tree file the installer wrote at first run). Meanwhile, hermes config show reports its secrets file as %USERPROFILE%\.hermes\.env — and that's the file users actually edit when they follow the API-server setup doc:

# website/docs/user-guide/features/api-server.md
Add to `~/.hermes/.env`:

  API_SERVER_ENABLED=true
  API_SERVER_KEY=...

~/.hermes/.env is Python Path.home() / '.hermes' / '.env' = C:\Users\<user>\.hermes\.env. That's NOT what load_hermes_dotenv reads when HERMES_HOME is set.

Result: the user follows the documented setup, writes API_SERVER_ENABLED=true to the file hermes config show calls "Secrets", restarts the gateway — and the API server stays off because the dotenv loader read a different file that never had the key. Stderr ends with WARNING gateway.run: No messaging platforms enabled.

Root cause

hermes_cli/env_loader.py (location confirmed via inspect.getsourcefile):

home_path = Path(hermes_home or os.getenv("HERMES_HOME", Path.home() / ".hermes"))
user_env = home_path / ".env"

When HERMES_HOME is set to a path that is NOT ~/.hermes (e.g. the install-tree %LOCALAPPDATA%\hermes), user_env resolves to a different file than the one hermes config show reports as the secrets path. The two should agree.

Diagnostic output (verbatim, from prettyandpink 2026-05-23)

=== dotenv diagnostic - read-only ===
  Host: PRETTYANDPINK
  OS:   Microsoft Windows 11 Pro
  PS:   7.6.2
  hermes: C:\Users\anf07\AppData\Local\hermes\hermes-agent\venv\Scripts\hermes.exe
  python: C:\Users\anf07\AppData\Local\hermes\hermes-agent\venv\Scripts\python.exe

--- 1. Path.home() resolution ---
Path.home() = C:\Users\anf07
USERPROFILE = C:\Users\anf07
HOMEDRIVE   = C:
HOMEPATH    = \Users\anf07

--- 2. load_hermes_dotenv signature + location ---
found: C:\Users\anf07\AppData\Local\hermes\hermes-agent\hermes_cli\env_loader.py
signature: (*, hermes_home: 'str | os.PathLike | None' = None, project_env: 'str | os.PathLike | None' = None) -> 'list[Path]'

--- 3. Where does config.py read API_SERVER_ENABLED? ---
  …\gateway\config.py L1522:
    api_server_enabled = os.getenv("API_SERVER_ENABLED", "").lower() in {"true", "1", "yes"}
  …\gateway\run.py L560:
    load_hermes_dotenv(hermes_home=_hermes_home, project_env=Path(__file__).resolve().parents[1] / '.env')

--- 4. ~/.hermes/.env content + encoding ---
  size: 158 bytes
  BOM: no (clean)
  line endings: 1 CR, 3 LF (MIXED)
  text content:
    HERMES_WEB_DIST=C:\Users\anf07\AppData\Local\hermes\hermes-agent\hermes_cli\web_dist
    API_SERVER_ENABLED=true
    API_SERVER_KEY=YHNxKMmBbkDLGV8bcoHdYhzPenOz34BC

--- 5. Env var state, three scopes ---
  API_SERVER_ENABLED     Process=(unset)   User=(set, 4 chars)    Machine=(unset)
  API_SERVER_KEY         Process=(unset)   User=(set, 32 chars)   Machine=(unset)
  HERMES_HOME            Process=(set)     User=(set, 35 chars)   Machine=(unset)
  USERPROFILE            Process=(set)     User=(unset)           Machine=(unset)

--- 6. Simulate the load-order race ---
  Spawn a fresh Python process that imports gateway.config WITHOUT first loading .env,
  then prints whether API_SERVER_ENABLED is visible.
imported gateway.config OK
pre-import,  API_SERVER_ENABLED = None
post-import, API_SERVER_ENABLED = None

Key observations:

  • Path.home() / '.hermes' / '.env' = C:\Users\anf07\.hermes\.env (158 bytes, our manual edit, contains API_SERVER_ENABLED=true)
  • HERMES_HOME = C:\Users\anf07\AppData\Local\hermes (35 chars, set at User scope by the installer)
  • load_hermes_dotenv resolves user_env via HERMES_HOMEC:\Users\anf07\AppData\Local\hermes\.env (different file, larger, doesn't contain API_SERVER_ENABLED)
  • Section 6 confirms gateway.config reads env vars correctly at function-call time (not import time), so the read order is fine — the file resolution is what's wrong

Suggested fix

In env_loader.load_hermes_dotenv, either:

  1. Drop the HERMES_HOME lookup for .env resolution entirely, always use Path.home() / '.hermes' / '.env' (the path hermes config show reports). HERMES_HOME would still control the broader Hermes data directory but not specifically the secrets .env.

  2. OR sanity-check that home_path / '.env' is the same file hermes config show reports, and warn / fall back to Path.home() / '.hermes' / '.env' if they diverge.

  3. OR document explicitly that HERMES_HOME overrides the secrets-file location too, and update the api-server setup doc to mention "if you have HERMES_HOME set, edit ${HERMES_HOME}/.env instead of ~/.hermes/.env."

Option 1 is least surprising. Option 3 is the smallest change but pushes the surprise to the user.

Workaround (what we deployed)

User-scope env vars via [Environment]::SetEnvironmentVariable('API_SERVER_KEY', $value, 'User'). Process inherits at logon (or any Start-Process if the parent shell has the var loaded), so os.getenv sees the value regardless of which .env file load_hermes_dotenv read. Codified in Foundry/tools/remote-agent/enable-api-server.ps1 and bind-tailscale.ps1.

Secondary observation (not the root cause but worth flagging)

The ~/.hermes/.env content has mixed line endings (1 CR, 3 LF per section 4). python-dotenv 1.x handles this, but the installer's .env-writer should normalize to platform-native line endings on Windows.

Platforms

  • Windows 11 Pro (build 26200), hermes-agent v0.14.0 — confirmed
  • Windows 11 Pro (build 26200, second machine, no HERMES_HOME set in User scope) — NOT affected (gateway boots clean from ~/.hermes/.env). This is consistent with the diagnosis: only machines where the installer sets HERMES_HOME to a non-~/.hermes path are affected.
  • Linux / macOS — not tested

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix env_loader.load_hermes_dotenv resolves ~/.hermes/.env via HERMES_HOME, disagrees with hermes config show