{"repo":"NousResearch/hermes-agent","public_id":"7976","display_label":"Gateway systemd service Python environment path issues","summary":"Most items concern the gateway's systemd service unit pointing to the wrong Python executable—system Python, hardcoded paths, or uv Python instead of the virtualenv's Python. A handful of adjacent items touch Nix/Hermes wrapper handling, startup exit codes, and minor unrelated fixes bundled into the same PRs.","subsystem":"environments","counts":{"members":15,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":8,"merged_prs":2,"attempt_prs":5},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":8861},"resolution":{"state":"partially_resolved","by":"pr-10093"},"freshness":{"state":"stale","last_verified_at":"2026-04-15T05:38:17+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.12076190476190474},"fix_edges":[{"issue_number":7976,"pr_number":8861,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":8519,"pr_number":8861,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":8620,"pr_number":10093,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":7976,"title":"[Bug]: gateway install --system ExecStart skips project venv","url":null,"state":"closed"},{"kind":"issue","number":8475,"title":"[Setup]: gateway startup seems to fail with an error exit code","url":null,"state":"closed"},{"kind":"issue","number":8519,"title":"Systemd service uses resolved Python path instead of venv symlink, breaking module imports","url":null,"state":"closed"},{"kind":"issue","number":8620,"title":"Gateway restart failures: wrong Python env in systemd, immediate exit after --replace, context_length not read, compression model 503","url":null,"state":"closed"},{"kind":"issue","number":9013,"title":"gateway install --system uses system Python instead of venv Python in ExecStart","url":null,"state":"closed"},{"kind":"issue","number":9021,"title":"PR: fix system service venv python path (Issue #9013)","url":null,"state":"closed"},{"kind":"issue","number":9201,"title":"[Bug]: systemd system service fails on boot: ExecStart uses uv Python instead of venv Python","url":null,"state":"closed"},{"kind":"issue","number":48071,"title":"[Bug]: Nix level-1 gateway install generates systemd unit that bypasses Hermes wrapper","url":null,"state":"closed"},{"kind":"pr","number":7985,"title":"fix(gateway): fall back to python3 when venv lacks bare python symlink","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#8861","number":8861},{"kind":"pr","value":"#7735","number":7735}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":8522,"title":"fix: preserve venv symlink in systemd service ExecStart path","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#8861","number":8861},{"kind":"pr","value":"#7735","number":7735}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":8861,"title":"fix(gateway): keep venv python symlink unresolved in systemd unit","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/gateway.py"],"fix_hunks":[{"file":"hermes_cli/gateway.py","hunks":["@@ -768,14 +768,22 @@ def _remap_path_for_user(path: str, target_home_dir: str) -> str:\n \n       /root/.hermes/hermes-agent  -> /home/alice/.hermes/hermes-agent\n       /opt/hermes                 -> /opt/hermes  (kept as-is)\n+\n+    Note: this function intentionally does NOT resolve symlinks. A venv's\n+    ``bin/python`` is typically a symlink to the base interpreter (e.g. a\n+    uv-managed CPython at ``~/.local/share/uv/python/.../python3.11``);\n+    resolving that symlink swaps the unit's ``ExecStart`` to a bare Python\n+    that has none of the venv's site-packages, so the service crashes on\n+    the first ``import``. Keep the symlinked path so the venv activates\n+    its own environment. Lexical expansion only via ``expanduser``.\n     \"\"\"\n-    current_home = Path.home().resolve()\n-    resolved = Path(path).resolve()\n+    current_home = Path.home()\n+    p = Path(path).expanduser()\n     try:\n-        relative = resolved.relative_to(current_home)\n+        relative = p.relative_to(current_home)\n         return str(Path(target_home_dir) / relative)\n     except ValueError:\n-        return str(resolved)\n+        return str(p)\n \n \n def _hermes_home_for_target_user(target_home_dir: str) -> str:\n"]}]},{"kind":"pr","number":9230,"title":"fix(gateway): use venv Python for systemd ExecStart instead of sys.executable (#9201)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"73ed09e145826c3521dc770ffefeb33b3efa1a9f","number":0},{"kind":"pr","value":"#9629","number":9629}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":9629,"title":"fix(gateway): systemd Python path fallback","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#9230","number":9230}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":10093,"title":"fix: compression model falls back to main model on permanent 503/404 (#8620)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/context_compressor.py"],"fix_hunks":[{"file":"agent/context_compressor.py","hunks":["@@ -693,6 +693,7 @@ def _generate_summary(self, turns_to_summarize: List[Dict[str, Any]], focus_topi\n             # Store for iterative updates on next compaction\n             self._previous_summary = summary\n             self._summary_failure_cooldown_until = 0.0\n+            self._summary_model_fallen_back = False\n             return self._with_summary_prefix(summary)\n         except RuntimeError:\n             # No provider configured — long cooldown, unlikely to self-resolve","@@ -703,6 +704,34 @@ def _generate_summary(self, turns_to_summarize: List[Dict[str, Any]], focus_topi\n                             _SUMMARY_FAILURE_COOLDOWN_SECONDS)\n             return None\n         except Exception as e:\n+            # If the summary model is different from the main model and the\n+            # error looks permanent (model not found, 503, 404), fall back to\n+            # using the main model instead of entering cooldown that leaves\n+            # context growing unbounded.  (#8620 sub-issue 4)\n+            _status = getattr(e, \"status_code\", None) or getattr(getattr(e, \"response\", None), \"status_code\", None)\n+            _err_str = str(e).lower()\n+            _is_model_not_found = (\n+                _status in (404, 503)\n+                or \"model_not_found\" in _err_str\n+                or \"does not exist\" in _err_str\n+                or \"no available channel\" in _err_str\n+            )\n+            if (\n+                _is_model_not_found\n+                and self.summary_model\n+                and self.summary_model != self.model\n+                and not getattr(self, \"_summary_model_fallen_back\", False)\n+            ):\n+                self._summary_model_fallen_back = True\n+                logging.warning(\n+                    \"Summary model '%s' not available (%s). \"\n+                    \"Falling back to main model '%s' for compression.\",\n+                    self.summary_model, e, self.model,\n+                )\n+                self.summary_model = \"\"  # empty = use main model\n+                self._summary_failure_cooldown_until = 0.0  # no cooldown\n+                return self._generate_summary(messages, summary_budget)  # retry immediately\n+\n             # Transient errors (timeout, rate limit, network) — shorter cooldown\n             _transient_cooldown = 60\n             self._summary_failure_cooldown_until = time.monotonic() + _transient_cooldown\n"]}]},{"kind":"pr","number":48086,"title":"fix(gateway): use hermes wrapper as systemd ExecStart on Nix installs (#48071)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#48071","number":48071},{"kind":"pr","value":"#33663","number":33663},{"kind":"pr","value":"#33663","number":33663}],"category":"duplicate_of","implication":"duplicate"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}