Bug Description
On Windows, hermes update always fails with Another hermes.exe is running: PID <X> hermes.exe, even when no other Hermes process exists on the system. The reported PID belongs to the very hermes.exe
console-script launcher that started the current update invocation. hermes update --force is the only way through, and it works cleanly — confirming nothing else is actually holding the venv shim.
Environment
- OS: Windows 11 Enterprise 22H2 (10.0.22621)
- Shell: Git Bash 2.x and PowerShell 5.1 (reproduces in both)
- Hermes Agent:
v0.14.0 (2026.5.16) (also reproduced on builds prior to this)
- Python: 3.11.15 (venv at
D:\AI\hermes\hermes-agent\venv)
- Install path:
D:\AI\hermes\hermes-agent (non-default — README's default is %LOCALAPPDATA%\hermes, but the install script accepted this location and hermes update works under --force, so the
path itself is not the issue)
- Hermes Desktop: NOT installed
- No WSL2 involved — pure native Windows install
Reproduction
- Confirm zero Hermes-related processes are alive:
Get-CimInstance Win32_Process | Where-Object {
$_.Name -like '*hermes*' -or $_.ExecutablePath -like '*hermes*'
}
# returns nothing
Get-Process python,pythonw -ErrorAction SilentlyContinue
# returns nothing
- Confirm gateway is stopped (hermes gateway stop is a no-op).
- Run hermes update from a fresh shell.
- Output:
? Another hermes.exe is running:
PID 18608 hermes.exe
Updating now would fail to overwrite D:\AI\hermes\hermes-agent\venv\Scripts\hermes.exe because
Windows blocks REPLACE on a running executable.
Close Hermes Desktop, exit any open `hermes` REPLs, and
stop the gateway (`hermes gateway stop`) before retrying.
Override with `hermes update --force` if you've already
confirmed those processes will not write to the venv.
- Immediately after exit, PID 18608 is no longer alive (Get-CimInstance Win32_Process -Filter "ProcessId=18608" returns nothing) — the "concurrent" process was the parent launcher, which exited together
with the failed update.
- hermes update --force then succeeds and updates cleanly. Re-running plain hermes update reproduces the failure with a new PID every time.
Root cause (suspected)
hermes_cli/main.py:7256 _detect_concurrent_hermes_instances excludes only os.getpid(). On Windows, hermes.exe is a distlib-generated console-script launcher (Scripts\hermes.exe). Its runtime model:
- User runs hermes update → hermes.exe launcher process starts (this is the PID later reported as "concurrent", e.g. 18608).
- Launcher spawns a child python.exe -m hermes_cli ... and stays alive, waiting on the child.
- Detection runs inside the child Python process. os.getpid() returns the Python process PID, not the launcher PID.
- psutil.process_iter sees the launcher's exe resolves to the same path as Scripts\hermes.exe, the launcher PID is not in exclude_pid, so it is reported as "another hermes.exe is running".
The doc-comment block above the function calls out Hermes Desktop's child-process scenario, but the simpler everyday case — the launcher that started the current invocation — appears to be unhandled.
Suggested fix
Exclude the immediate parent PID as well, and ideally any ancestor whose exe resolves to one of the shim paths:
exclude_pids: set[int] = {os.getpid()}
try:
import psutil
proc = psutil.Process()
while True:
parent = proc.parent()
if parent is None:
break
try:
parent_exe = str(Path(parent.exe()).resolve()).lower()
except (psutil.Error, OSError, ValueError):
break
if parent_exe not in shim_paths:
break
exclude_pids.add(parent.pid)
proc = parent
except Exception:
pass
Then check pid not in exclude_pids instead of pid == exclude_pid in the match loop.
Diagnostics already ruled out
While debugging this we also found D:\AI\hermes\gateway_state.json was stuck at gateway_state: "running" with updated_at: 2026-05-13 (a separate stale-lifecycle bug — gateway never wrote stopped after a
previous unclean shutdown). Removing/renaming that file did not affect the update gating, confirming the detector reads only live process state, not that JSON. Worth noting as a separate, smaller issue:
hermes gateway stop and/or update flows should normalize this state file.
Workaround
hermes update --force — works on every invocation in this environment. Documenting that it's the expected path on Windows until the parent-PID exclusion lands would help.
Steps to Reproduce
Run hermes update
Expected Behavior
Running hermes update on Windows when no other Hermes processes exist (no Desktop, no REPLs, no gateway, no orphaned children) should proceed with the update without requiring --force.
The console-script launcher process (Scripts\hermes.exe) that started the current update invocation is part of the same logical command and should not be detected as a "concurrent instance". The detector
should exclude both os.getpid() and any ancestor process whose executable resolves to one of the shim paths.
--force should remain available as an escape hatch for genuinely concurrent scenarios (Hermes Desktop child, second REPL), not as the default-required path for clean systems.
Actual Behavior
hermes update aborts with exit code 2 every time, reporting a "concurrent" hermes.exe PID. That PID is the launcher shim that spawned the current update Python process; it exits together with the
failed update, so by the time the user investigates, the PID is already gone. hermes update --force then succeeds without any actual concurrency conflict.
Affected Component
CLI (interactive chat)
Messaging Platform (if gateway-related)
No response
Debug Report
Security Advisories
No active security advisories
Python Environment
Python 3.11.15
Virtual environment active
Required Packages
OpenAI SDK
Rich (terminal UI)
python-dotenv
PyYAML
HTTPX
Croniter (cron expressions) (optional)
python-telegram-bot (optional, not installed)
discord.py (optional, not installed)
Configuration Files
D:\AI\hermes/.env file exists
API key or custom endpoint configured
D:\AI\hermes/config.yaml exists
Config version up to date (v23)
Auth Providers
Nous Portal auth (not logged in)
OpenAI Codex auth (not logged in)
No Codex credentials stored. Run `hermes auth` to authenticate.
codex CLI not installed (optional only required to import tokens from an existing Codex CLI login)
Google Gemini OAuth (not logged in)
MiniMax OAuth (not logged in)
xAI OAuth (not logged in)
No xAI OAuth credentials stored. Select xAI Grok OAuth (SuperGrok Subscription) in `hermes model`.
Directory Structure
D:\AI\hermes directory exists
D:\AI\hermes/cron/ exists
D:\AI\hermes/sessions/ exists
D:\AI\hermes/logs/ exists
D:\AI\hermes/skills/ exists
D:\AI\hermes/memories/ exists
D:\AI\hermes/SOUL.md exists (persona configured)
D:\AI\hermes/memories/ directory exists
MEMORY.md exists (549 chars)
USER.md exists (669 chars)
D:\AI\hermes/state.db exists (44 sessions)
External Tools
git
ripgrep (rg) (faster file search)
docker not found (optional)
Node.js
agent-browser (Node.js) (browser automation)
Playwright Chromium not installed (browser_* tools will be hidden from the agent)
Install with: cd D:\AI\hermes\hermes-agent && npx playwright install chromium
Browser tools (agent-browser) deps (no known vulnerabilities)
API Connectivity
Running 27 connectivity checks in parallel
OpenRouter API
Anthropic API (invalid API key)
Tool Availability
clarify
code_execution
cronjob
terminal
delegation
file
memory
moa
session_search
skills
todo
tts
vision
video
kanban (runtime-gated; loaded only for dispatcher-spawned workers)
browser (system dependency not met)
browser-cdp (system dependency not met)
computer_use (system dependency not met)
discord (missing DISCORD_BOT_TOKEN)
discord_admin (missing DISCORD_BOT_TOKEN)
feishu_doc (system dependency not met)
feishu_drive (system dependency not met)
homeassistant (system dependency not met)
image_gen (system dependency not met)
messaging (system dependency not met)
video_gen (system dependency not met)
web (missing EXA_API_KEY, PARALLEL_API_KEY, TAVILY_API_KEY, FIRECRAWL_API_KEY, FIRECRAWL_API_URL, FIRECRAWL_GATEWAY_URL, TOOL_GATEWAY_DOMAIN, TOOL_GATEWAY_SCHEME, TOOL_GATEWAY_USER_TOKEN)
x_search (missing XAI_API_KEY)
hermes-yuanbao (system dependency not met)
spotify (system dependency not met)
Skills Hub
Skills Hub directory not initialized (run: hermes skills list)
No GITHUB_TOKEN (60 req/hr rate limit set in D:\AI\hermes/.env for better rates)
Memory Provider
Built-in memory active (no external provider configured this is fine)
Found 1 issue(s) to address:
1. Run 'hermes setup' to configure missing API keys for full tool access
Tip: run 'hermes doctor --fix' to auto-fix what's possible.
Operating System
Windows 11
Python Version
3.11.15
Hermes Version
V0.14.0(2026.5.16)
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?
Bug Description
On Windows,
hermes updatealways fails withAnother hermes.exe is running: PID <X> hermes.exe, even when no other Hermes process exists on the system. The reported PID belongs to the veryhermes.execonsole-script launcher that started the current
updateinvocation.hermes update --forceis the only way through, and it works cleanly — confirming nothing else is actually holding the venv shim.Environment
v0.14.0 (2026.5.16)(also reproduced on builds prior to this)D:\AI\hermes\hermes-agent\venv)D:\AI\hermes\hermes-agent(non-default — README's default is%LOCALAPPDATA%\hermes, but the install script accepted this location andhermes updateworks under--force, so thepath itself is not the issue)
Reproduction
? Another hermes.exe is running:
PID 18608 hermes.exe
with the failed update.
Root cause (suspected)
hermes_cli/main.py:7256 _detect_concurrent_hermes_instances excludes only os.getpid(). On Windows, hermes.exe is a distlib-generated console-script launcher (Scripts\hermes.exe). Its runtime model:
The doc-comment block above the function calls out Hermes Desktop's child-process scenario, but the simpler everyday case — the launcher that started the current invocation — appears to be unhandled.
Suggested fix
Exclude the immediate parent PID as well, and ideally any ancestor whose exe resolves to one of the shim paths:
exclude_pids: set[int] = {os.getpid()}
try:
import psutil
proc = psutil.Process()
while True:
parent = proc.parent()
if parent is None:
break
try:
parent_exe = str(Path(parent.exe()).resolve()).lower()
except (psutil.Error, OSError, ValueError):
break
if parent_exe not in shim_paths:
break
exclude_pids.add(parent.pid)
proc = parent
except Exception:
pass
Then check pid not in exclude_pids instead of pid == exclude_pid in the match loop.
Diagnostics already ruled out
While debugging this we also found D:\AI\hermes\gateway_state.json was stuck at gateway_state: "running" with updated_at: 2026-05-13 (a separate stale-lifecycle bug — gateway never wrote stopped after a
previous unclean shutdown). Removing/renaming that file did not affect the update gating, confirming the detector reads only live process state, not that JSON. Worth noting as a separate, smaller issue:
hermes gateway stop and/or update flows should normalize this state file.
Workaround
hermes update --force — works on every invocation in this environment. Documenting that it's the expected path on Windows until the parent-PID exclusion lands would help.
Steps to Reproduce
Run hermes update
Expected Behavior
Running
hermes updateon Windows when no other Hermes processes exist (no Desktop, no REPLs, no gateway, no orphaned children) should proceed with the update without requiring--force.The console-script launcher process (
Scripts\hermes.exe) that started the currentupdateinvocation is part of the same logical command and should not be detected as a "concurrent instance". The detectorshould exclude both
os.getpid()and any ancestor process whose executable resolves to one of the shim paths.--forceshould remain available as an escape hatch for genuinely concurrent scenarios (Hermes Desktop child, second REPL), not as the default-required path for clean systems.Actual Behavior
hermes updateaborts with exit code 2 every time, reporting a "concurrent"hermes.exePID. That PID is the launcher shim that spawned the currentupdatePython process; it exits together with thefailed update, so by the time the user investigates, the PID is already gone.
hermes update --forcethen succeeds without any actual concurrency conflict.Affected Component
CLI (interactive chat)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Windows 11
Python Version
3.11.15
Hermes Version
V0.14.0(2026.5.16)
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?