Hardcoded venv path bugs and fixes across CLI/gateway/docs

NousResearch/hermes-agent · cluster 2066 · docs

Data as of

10 open items. Fix merged in #2114. Check it covers your case.

The short answer

4open issues
6open PRs
39items grouped
Known?
Yes. 39 related items grouped here, 5 issues already closed.
Fix event
A fix was merged to main in PR #2114. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PRs #2114, #2797, #123011. That is separate from the fix event.
Still open
4 issues and 6 PRs.

Multiple bugs and corresponding fixes address hardcoded 'venv' directory assumptions throughout the CLI, gateway, doctor, and docs, which break when the active virtualenv is named '.venv' or located elsewhere. Several related issues cover Brew installation conflicts, systemd unit generation, and doctor false-positives stemming from the same path detection gap.

Open issues4

  • Issue#35293doctor: Homebrew install falsely warns 'Venv entry point not found'
  • Issue#43250Bug: _venv_scripts_dir() only checks "venv", ignores ".venv"
  • Issue#49529[Bug]: PyPI 0.17.0 wheel install: doctor false-positive venv entry point warning and optional-skills missing from wheel
  • Issue#50581bug: doctor/update guidance assumes PROJECT_ROOT/venv and misses active sidecar venv

Open PRs6

  • PR#35300fix(doctor): recognize Homebrew/uv-tool installs in venv entry point check
  • PR#35303fix(doctor): restrict managed-install detection to docker and nixos
  • PR#43303fix(doctor): align command-link check with install.sh layouts (Termux, root/FHS, isolated HOME)
  • PR#50592fix(cli): respect active sidecar venv paths
  • PR#50704Fix update paths for project .venv installs
  • PR#61076fix(cli): detect hermes root in package installations (Brew, pip)

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #2114 docs: align venv path to match installer (venv/ not .venv/)

    merged to main

    files: AGENTS.md, README.md, skills/dogfood/hermes-agent-setup/SKILL.md, skills/mlops/training/hermes-atropos-environments/references/usage-patterns.md, website/docs/developer-guide/adding-providers.md

  • #2797 fix(gateway): detect virtualenv path instead of hardcoding venv/ (#2492)

    merged to main

    files: hermes_cli/gateway.py, tests/hermes_cli/test_gateway_service.py

    Show the diff
    • hermes_cli/gateway.py
      @@ -371,13 +371,37 @@ def print_systemd_linger_guidance() -> None:
       def get_launchd_plist_path() -> Path:
           return Path.home() / "Library" / "LaunchAgents" / "ai.hermes.gateway.plist"
       
      +def _detect_venv_dir() -> Path | None:
      +    """Detect the active virtualenv directory.
      +
      +    Checks ``sys.prefix`` first (works regardless of the directory name),
      +    then falls back to probing common directory names under PROJECT_ROOT.
      +    Returns ``None`` when no virtualenv can be found.
      +    """
      +    # If we're running inside a virtualenv, sys.prefix points to it.
      +    if sys.prefix != sys.base_prefix:
      +        venv = Path(sys.prefix)
      +        if venv.is_dir():
      +            return venv
      +
      +    # Fallback: check common virtualenv directory names under the project root.
      +    for candidate in (".venv", "venv"):
      +        venv = PROJECT_ROOT / candidate
      +        if venv.is_dir():
      +            return venv
      +
      +    return None
      +
      +
       def get_python_path() -> str:
      -    if is_windows():
      -        venv_python = PROJECT_ROOT / "venv" / "Scripts" / "python.exe"
      -    else:
      -        venv_python = PROJECT_ROOT / "venv" / "bin" / "python"
      -    if venv_python.exists():
      -        return str(venv_python)
      +    venv = _detect_venv_dir()
      +    if venv is not None:
      +        if is_windows():
      +            venv_python = venv / "Scripts" / "python.exe"
      +        else:
      +            venv_python = venv / "bin" / "python"
      +        if venv_python.exists():
      +            return str(venv_python)
           return sys.executable
       
       def get_hermes_cli_path() -> str:
      @@ -399,8 +423,9 @@ def get_hermes_cli_path() -> str:
       def generate_systemd_unit(system: bool = False, run_as_user: str | None = None) -> str:
           python_path = get_python_path()
           working_dir = str(PROJECT_ROOT)
      -    venv_dir = str(PROJECT_ROOT / "venv")
      -    venv_bin = str(PROJECT_ROOT / "venv" / "bin")
      +    detected_venv = _detect_venv_dir()
      +    venv_dir = str(detected_venv) if detected_venv else str(PROJECT_ROOT / "venv")
      +    venv_bin = str(detected_venv / "bin") if detected_venv else str(PROJECT_ROOT / "venv" / "bin")
           node_bin = str(PROJECT_ROOT / "node_modules" / ".bin")
       
           path_entries = [venv_bin, node_bin]
    • tests/hermes_cli/test_gateway_service.py
      @@ -282,6 +282,78 @@ def test_gateway_restart_does_not_fallback_to_foreground_when_launchd_restart_fa
               assert run_calls == []
       
       
      +class TestDetectVenvDir:
      +    """Tests for _detect_venv_dir() virtualenv detection."""
      +
      +    def test_detects_active_virtualenv_via_sys_prefix(self, tmp_path, monkeypatch):
      +        venv_path = tmp_path / "my-custom-venv"
      +        venv_path.mkdir()
      +        monkeypatch.setattr("sys.prefix", str(venv_path))
      +        monkeypatch.setattr("sys.base_prefix", "/usr")
      +
      +        result = gateway_cli._detect_venv_dir()
      +        assert result == venv_path
      +
      +    def test_falls_back_to_dot_venv_directory(self, tmp_path, monkeypatch):
      +        # Not inside a virtualenv
      +        monkeypatch.setattr("sys.prefix", "/usr")
      +        monkeypatch.setattr("sys.base_prefix", "/usr")
      +        monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", tmp_path)
      +
      +        dot_venv = tmp_path / ".venv"
      +        dot_venv.mkdir()
      +
      +        result = gateway_cli._detect_venv_dir()
      +        assert result == dot_venv
      +
      +    def test_falls_back_to_venv_directory(self, tmp_path, monkeypatch):
      +        monkeypatch.setattr("sys.prefix", "/usr")
      +        monkeypatch.setattr("sys.base_prefix", "/usr")
      +        monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", tmp_path)
      +
      +        venv = tmp_path / "venv"
      +        venv.mkdir()
      +
      +        result = gateway_cli._detect_venv_dir()
      +        assert result == venv
      +
      +    def test_prefers_dot_venv_over_venv(self, tmp_path, monkeypatch):
      +        monkeypatch.setattr("sys.prefix", "/usr")
      +        monkeypatch.setattr("sys.base_prefix", "/usr")
      +        monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", tmp_path)
      +
      +        (tmp_path / ".venv").mkdir()
      +        (tmp_path / "venv").mkdir()
      +
      +        result = gateway_cli._detect_venv_dir()
      +        assert result == tmp_path / ".venv"
      +
      +    def test_returns_none_when_no_virtualenv(self, tmp_path, monkeypatch):
      +        monkeypatch.setattr("sys.prefix", "/usr")
      +        monkeypatch.setattr("sys.base_prefix", "/usr")
      +        monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", tmp_path)
      +
      +        result = gateway_cli._detect_venv_dir()
      +        assert result is None
      +
      +
      +class TestGeneratedUnitUsesDetectedVenv:
      +    def test_systemd_unit_uses_dot_venv_when_detected(self, tmp_path, monkeypatch):
      +        dot_venv = tmp_path / ".venv"
      +        dot_venv.mkdir()
      +        (dot_venv / "bin").mkdir()
      +
      +        monkeypatch.setattr(gateway_cli, "_detect_venv_dir", lambda: dot_venv)
      +        monkeypatch.setattr(gateway_cli, "get_python_path", lambda: str(dot_venv / "bin" / "python"))
      +
      +        unit = gateway_cli.generate_systemd_unit(system=False)
      +
      +        assert f"VIRTUAL_ENV={dot_venv}" in unit
      +        assert f"{dot_venv}/bin" in unit
      +        # Must NOT contain a hardcoded /venv/ path
      +        assert "/venv/" not in unit or "/.venv/" in unit
      +
      +
       class TestEnsureUserSystemdEnv:
           """Tests for _ensure_user_systemd_env() D-Bus session bus auto-detection."""
       
  • #123011 fix(desktop): handle package-manager installs with no desktop source tree

    merged to main

    files: hermes_cli/main_desktop.py, tests/hermes_cli/test_gui_command.py

21 PRs closed without merging

From the close comments: won't fix / by design 10 · merged via another change 3 · closed without comment 1 · done elsewhere 1 · stale or abandoned 1 · superseded by another PR 1 · other 4

  • #2087 fix(docs): handle both venv and .venv in AGENTS.md activation command · other
  • #2493 fix(gateway): detect virtualenv path instead of hardcoding venv/ · merged via another change → #2797
  • #2500 fix(gateway): detect .venv and venv directories for systemd unit generation · merged via another change → #2493, #2797
  • #2518 fix(gateway): dynamically detect virtualenv path in systemd unit generation (#2492) · merged via another change → #2493, #2797
  • #39735 fix(cli): resolve active venv directory dynamically instead of hardcoding "venv" · won't fix / by design
Show 16 more
  • #39749 fix(update): resolve venv from active interpreter instead of hardcoded path · won't fix / by design
  • #39752 fix(cli): target active venv during source update · closed without comment
  • #40191 fix(cli): repair hermes update path (venv detection, uv arity, v25→v26 migration) · other
  • #40207 fix(cli): resolve the real venv root in hermes update instead of hardcoded venv/ · won't fix / by design
  • #40994 Fix #39714: Replace hardcoded venv paths with detection · won't fix / by design
  • #43258 fix(cli): check both venv and .venv directories in _venv_scripts_dir() · won't fix / by design
  • #43262 fix(cli): make _venv_scripts_dir() check .venv before venv · won't fix / by design
  • #43374 fix(cli): check both venv and .venv directories for script shims (#43250) · superseded by another PR → #43258, #43250
  • #43707 fix: detect .venv in _venv_scripts_dir() and block cronjob toolset in child agents · won't fix / by design
  • #49566 fix(cli): resolve false-positive venv entry point warning in doctor (#49529) · other
  • #49666 fix(windows): detect .venv and use sys.prefix for venv path resolution · won't fix / by design
  • #49842 fix(update): resolve venv dir for both `venv` and `.venv` layouts (#49665) · won't fix / by design
  • #61085 fix: resolve desktop GUI source via HERMES_DESKTOP_SOURCE env var for managed installs (#61056) · stale or abandoned
  • #62191 fix(update): resolve venv dir for both venv/ and .venv/ layouts · won't fix / by design → 3700dd5
  • #77428 fix(doctor): detect environment-installed entry points (no false 'Venv entry point not found') · done elsewhere → e6c862d
  • #99478 fix(desktop): give actionable error when Electron source tree absent from wheel/Brew installs · other → #99443, #99448
Closed issues · 5
  • Closed#2066[Bug]: wrong venv path?
  • Closed#2492bug(gateway): systemd service unit hardcodes venv/ path, breaks when virtualenv is .venv
  • Closed#39714Bug: hermes update installs deps into hardcoded venv/ while active env is .venv (duplicate orphan virtualenv)
  • Closed#49665_venv_scripts_dir() hardcodes "venv" — silently disables quarantine and concurrent-instance detection when venv is at .venv
  • Closed#61056[Bug]: Installing hermes via Brew break the hermes desktop installation.
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.05: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
docs
Merged PRs
#2114, #2797, #123011
Closed issues
5
Attempts
21 PRs closed without merging
Fix link checked
25 Sep 2026

For your agent

The same facts, typed.