hermes - 💡(How to fix) Fix [Bug]: Windows: \hermes update` always reports "Another hermes.exe is running" — launcher shim PID not excluded from concurrent-instance detection`

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…

Error Message

Get-CimInstance Win32_Process | Where-Object { $.Name -like 'hermes' -or $.ExecutablePath -like 'hermes' } # returns nothing Get-Process python,pythonw -ErrorAction SilentlyContinue # returns nothing 2. Confirm gateway is stopped (hermes gateway stop is a no-op). 3. Run hermes update from a fresh shell. 4. 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.

5. 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. 6. 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:

  1. User runs hermes update → hermes.exe launcher process starts (this is the PID later reported as "concurrent", e.g. 18608).
  2. Launcher spawns a child python.exe -m hermes_cli ... and stays alive, waiting on the child.
  3. Detection runs inside the child Python process. os.getpid() returns the Python process PID, not the launcher PID.
  4. 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

Root Cause

Updating now would fail to overwrite D:\AI\hermes\hermes-agent\venv\Scripts\hermes.exe because Windows blocks REPLACE on a running executable.

Fix Action

Fix / Workaround

Workaround

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)

Code Example

Get-CimInstance Win32_Process | Where-Object {
         $_.Name -like '*hermes*' -or $_.ExecutablePath -like '*hermes*'
     }
     # returns nothing
     Get-Process python,pythonw -ErrorAction SilentlyContinue
     # returns nothing
  2. Confirm gateway is stopped (hermes gateway stop is a no-op).
  3. Run hermes update from a fresh shell.
  4. 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.
  5. 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.
  6. 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:

  1. User runs hermes update → hermes.exe launcher process starts (this is the PID later reported as "concurrent", e.g. 18608).
  2. Launcher spawns a child python.exe -m hermes_cli ... and stays alive, waiting on the child.
  3. Detection runs inside the child Python process. os.getpid() returns the Python process PID, not the launcher PID.
  4. 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

---

### Operating System

Windows 11

### Python Version

3.11.15

### Hermes Version

V0.14.0(2026.5.16)

### Additional Logs / Traceback (optional)
RAW_BUFFERClick to expand / collapse

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

  1. 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
  2. Confirm gateway is stopped (hermes gateway stop is a no-op).
  3. Run hermes update from a fresh shell.
  4. 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.

5. 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. 6. 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:

  1. User runs hermes update → hermes.exe launcher process starts (this is the PID later reported as "concurrent", e.g. 18608).
  2. Launcher spawns a child python.exe -m hermes_cli ... and stays alive, waiting on the child.
  3. Detection runs inside the child Python process. os.getpid() returns the Python process PID, not the launcher PID.
  4. 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?

  • I'd like to fix this myself and submit a PR

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