fix(constants): warn once when get_hermes_home() falls back under an active profile#18746
Merged
Conversation
…active profile When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs #18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR #18600; we kept the diagnostic signal without the import-time raise.
5 tasks
nickdlkk
pushed a commit
to nickdlkk/hermes-agent
that referenced
this pull request
May 11, 2026
…active profile (NousResearch#18746) When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (NousResearch#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs NousResearch#18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR NousResearch#18600; we kept the diagnostic signal without the import-time raise.
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
…active profile (NousResearch#18746) When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (NousResearch#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs NousResearch#18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR NousResearch#18600; we kept the diagnostic signal without the import-time raise.
This was referenced May 15, 2026
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
…active profile (NousResearch#18746) When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (NousResearch#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs NousResearch#18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR NousResearch#18600; we kept the diagnostic signal without the import-time raise.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…active profile (NousResearch#18746) When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (NousResearch#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs NousResearch#18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR NousResearch#18600; we kept the diagnostic signal without the import-time raise.
Egavasyug
pushed a commit
to Egavasyug/hermes-agent
that referenced
this pull request
Jun 10, 2026
…active profile (NousResearch#18746) When HERMES_HOME is unset but ~/.hermes/active_profile names a non-default profile, any data this process writes lands in the default profile — not the one the operator expects. Before this change the fallback was silent, so cross-profile contamination (NousResearch#18594) was invisible until a user noticed their memory/state ended up in the wrong place. Now we emit a one-shot warning to stderr the first time this happens in a process. No raise — there are 30+ module-level callers of get_hermes_home() and raising from any of them would brick import. Behavior is otherwise unchanged; subprocess spawners (systemd template, kanban dispatcher, docker entrypoint) already propagate HERMES_HOME correctly. Bypasses logging.getLogger() because this runs before logging is configured in a significant fraction of callers (module import time). Refs NousResearch#18594. Credit to @liuhao1024 for surfacing the silent-fallback case in PR NousResearch#18600; we kept the diagnostic signal without the import-time raise.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the ~/.hermes fallback visible when a non-default profile is active. Surfaces cross-profile data contamination (#18594) without breaking the 30+ module-level callers that import
get_hermes_home()at load time.What changed
hermes_constants.py: whenHERMES_HOMEis unset and~/.hermes/active_profilenames a non-default profile, write a one-shot warning to stderr and continue returning~/.hermesas before.tests/test_hermes_home_profile_warning.py: 6 test cases (classic mode, default profile, named profile, env-set-wins, unreadable active_profile, empty active_profile).Why not raise (superseding #18600)
The original PR proposal raised
ValueErrorfromget_hermes_home()when a named profile was active withoutHERMES_HOMEset. 30+ files call this at module-import scope (run_agent.py,gateway/run.py,cron/scheduler.py,hermes_cli/doctor.py,tools/skills_tool.py,acp_adapter/entry.py, etc.) — raising there would brick imports in every cron tick, subagent, and IDE launcher where HERMES_HOME didn't propagate for any reason. That failure mode is worse than the silent bleed it fixes. POSIX subprocesses actually DO inherit parent env by default, so the real propagation paths (systemd unitEnvironment=, kanban dispatcherenv=dict(os.environ), docker entrypoint) already work.The stderr write bypasses
loggingbecause this function runs before logging is configured in many of the import-time callers, and going through the root logger double-emits on consoles that already have a StreamHandler.Validation
scripts/run_tests.sh tests/test_hermes_home_profile_warning.py tests/hermes_cli/test_profiles.py→ 100 passed.HOMEredirect +active_profile=coder+HERMES_HOMEunset. 10 calls toget_hermes_home()→ exactly 1 warning emitted to stderr, fallback path still returned, module imports ofhermes_cli.gatewayandcron.schedulersucceed.Closes #18594. Credit to @liuhao1024 for surfacing the silent-fallback case in #18600.