hermes - 💡(How to fix) Fix Bug: hermes update installs deps into hardcoded venv/ while active env is .venv (duplicate orphan virtualenv)

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…

On a uv-based install, hermes update (the git/source path in _cmd_update_impl) installs Python dependencies into a hardcoded PROJECT_ROOT/"venv" directory, but the CLI launcher and all running processes actually use PROJECT_ROOT/".venv" (created by the uv installer). The result is two side-by-side virtualenvs: dependency updates land in venv/, which the running hermes never imports from. The active .venv/ silently drifts out of date.

This is distinct from #39444 (PyPI path not setting VIRTUAL_ENV at all) — here VIRTUAL_ENV is set, but to the wrong, hardcoded path. Both share the same fix direction: detect the active env via sys.prefix instead of assuming a path.

Root Cause

Several code paths hardcode PROJECT_ROOT / "venv" (no dot) instead of detecting the active interpreter's prefix:

  • hermes_cli/main.py:10560uv_env = {**os.environ, "VIRTUAL_ENV": str(PROJECT_ROOT / "venv")} (git update dep install in _cmd_update_impl)
  • hermes_cli/main.py:8043 — same pattern in the other update path
  • hermes_cli/main.py:8699_venv_scripts_dir() checks PROJECT_ROOT / "venv"
  • hermes_cli/gateway.py:2316 and :3023 — fall back to PROJECT_ROOT / "venv" when detected_venv is empty

On a uv install the real env is .venv, so every one of these misses it.

Fix Action

Workaround

rm -rf /opt/hermes-agent/venv
ln -s .venv /opt/hermes-agent/venv   # make the hardcoded path resolve to the real env

Code Example

launcher:  /root/.local/bin/hermes -> /opt/hermes-agent/.venv/bin/hermes
running:   4 processes (dashboard, gateway, main) all use /opt/hermes-agent/.venv

/opt/hermes-agent/.venv   681 packages   prompt=hermes-agent   (real, active env)
/opt/hermes-agent/venv    191 packages   no prompt             (orphan, dep updates land here)

---

# When running inside the venv, sys.prefix is the venv root.
venv_root = Path(sys.prefix) if sys.prefix != sys.base_prefix else (PROJECT_ROOT / ".venv" if (PROJECT_ROOT / ".venv").is_dir() else PROJECT_ROOT / "venv")

---

rm -rf /opt/hermes-agent/venv
ln -s .venv /opt/hermes-agent/venv   # make the hardcoded path resolve to the real env
RAW_BUFFERClick to expand / collapse

Summary

On a uv-based install, hermes update (the git/source path in _cmd_update_impl) installs Python dependencies into a hardcoded PROJECT_ROOT/"venv" directory, but the CLI launcher and all running processes actually use PROJECT_ROOT/".venv" (created by the uv installer). The result is two side-by-side virtualenvs: dependency updates land in venv/, which the running hermes never imports from. The active .venv/ silently drifts out of date.

This is distinct from #39444 (PyPI path not setting VIRTUAL_ENV at all) — here VIRTUAL_ENV is set, but to the wrong, hardcoded path. Both share the same fix direction: detect the active env via sys.prefix instead of assuming a path.

Environment

  • Hermes Agent v0.15.1 (2026.5.29)
  • Python 3.11.15, uv 0.11.x, Linux
  • Install type: source checkout under /opt/hermes-agent, uv-managed .venv

Observed state

launcher:  /root/.local/bin/hermes -> /opt/hermes-agent/.venv/bin/hermes
running:   4 processes (dashboard, gateway, main) all use /opt/hermes-agent/.venv

/opt/hermes-agent/.venv   681 packages   prompt=hermes-agent   (real, active env)
/opt/hermes-agent/venv    191 packages   no prompt             (orphan, dep updates land here)

All running processes and the launcher use .venv. Nothing references the bare venv at runtime — yet that is exactly where hermes update writes dependencies.

Root cause

Several code paths hardcode PROJECT_ROOT / "venv" (no dot) instead of detecting the active interpreter's prefix:

  • hermes_cli/main.py:10560uv_env = {**os.environ, "VIRTUAL_ENV": str(PROJECT_ROOT / "venv")} (git update dep install in _cmd_update_impl)
  • hermes_cli/main.py:8043 — same pattern in the other update path
  • hermes_cli/main.py:8699_venv_scripts_dir() checks PROJECT_ROOT / "venv"
  • hermes_cli/gateway.py:2316 and :3023 — fall back to PROJECT_ROOT / "venv" when detected_venv is empty

On a uv install the real env is .venv, so every one of these misses it.

Impact

  • Dependency updates never reach the env the CLI runs from; .venv drifts stale.
  • A wasted ~190 MB orphan venv/ accumulates.
  • Confusing to debug: hermes update reports success while the running CLI sees none of the new/updated deps.

Secondary issue (same workflow)

When there are no new commits, hermes update short-circuits with ✓ Already up to date! and skips the dependency reinstall entirely. So if an update aborts mid-way (e.g. the ValueError: too many values to unpack crash in #39706 / #39549), re-running hermes update cannot finish the interrupted dependency step — deps must be installed manually. Consider letting --force (or a new flag) re-run the dependency/build stages even when the git tree is already current; today --force is Windows-only.

Suggested fix

Resolve the target venv from the active interpreter rather than a fixed path, e.g.:

# When running inside the venv, sys.prefix is the venv root.
venv_root = Path(sys.prefix) if sys.prefix != sys.base_prefix else (PROJECT_ROOT / ".venv" if (PROJECT_ROOT / ".venv").is_dir() else PROJECT_ROOT / "venv")

and reuse that single resolver in the update paths, _venv_scripts_dir(), and the gateway fallbacks so .venv and venv installs are both handled consistently.

Workaround

rm -rf /opt/hermes-agent/venv
ln -s .venv /opt/hermes-agent/venv   # make the hardcoded path resolve to the real env

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