Skip to content

[Bug]: Desktop app silently exits when SingletonLock held by zombie/defunct Electron process (Linux/X11) #78101

Description

@johanruizb

Bug Description

When the Hermes Desktop Electron process becomes a zombie/defunct (e.g. after a crash, OOM kill, or kill -9), the SingletonLock symlink in ~/.config/Hermes/ still points to the dead PID. The next launch via hermes desktop (or the .desktop shortcut) silently exits with code 0 — no error message, no window, no log entry indicating the problem. The user sees nothing happen.

Steps to Reproduce

  1. Launch hermes desktop — it works, window appears
  2. Kill the Electron process abruptly: kill -9 <hermes-electron-pid>
  3. The SingletonLock symlink remains in ~/.config/Hermes/SingletonLock -> <hostname>-<dead-pid>
  4. Launch hermes desktop again (or click the .desktop shortcut)
  5. Nothing happens. The process exits immediately with code 0. No window, no error.

Expected Behavior

The app should detect the stale SingletonLock (PID is dead), clean it up, and launch normally. Or at minimum, log a message in desktop.log indicating the lock failure so the user can diagnose it.

Actual Behavior

The process exits silently with code 0. The desktop.log shows the backend boot completing successfully, then nothing — no error about the lock:

[hermes] [boot] Hermes backend is ready. Finalizing desktop startup

No further log entries. The Electron process quits because requestSingleInstanceLock() returned false, but this is not logged anywhere.

Affected Component

  • Other (Desktop / Electron app)

Operating System

Ubuntu 24.04, kernel 7.0.0-28-generic, GNOME on X11

Python Version

3.11.15

Hermes Version

0.19.1 (upstream 91937a6, local b1858f3, install method: git)

Debug Report

hermes debug share --local output (not uploaded to paste service to avoid exposing personal data from session logs):

version:          0.19.1 [b1858f33] (2026-07-31)
os:               Linux 7.0.0-28-generic x86_64
python:           3.11.15
openai_sdk:       2.24.0
profile:          default
hermes_home:      ~/.hermes
model:            glm-5.2
provider:         ollama-cloud
terminal:         local
features:
  toolsets:           hermes-cli, web
  mcp_servers:        0
  memory_provider:    built-in
  gateway:            running (docker (foreground), pid 1400)
  platforms:          none
  cron_jobs:          0
  skills:             129
config_overrides:
  agent.max_turns: 900
  display.streaming: True
  display.show_reasoning: False
  toolsets: [hermes-cli, web]

Additional Logs

~/.hermes/logs/desktop.log — repeated boot sequences showing the backend starting fine but the Electron process exiting silently:

[hermes] [boot] Resolving Hermes backend
[hermes] [boot] Hermes runtime is ready
[hermes] [boot] Starting Hermes backend via Hermes at /home/jr0237/.hermes/hermes-agent
[hermes] HERMES_BACKEND_READY port=43543
[hermes] Hermes backend listening on 127.0.0.1:43543
[hermes] [boot] Hermes backend is ready. Finalizing desktop startup
# ← process exits here, no further log entries, no error

The SingletonLock state observed:

$ ls -la ~/.config/Hermes/SingletonLock
lrwxrwxrwx 1 jr0237 jr0237 20 ago  3 20:44 ~/.config/Hermes/SingletonLock -> jr0237-MS-7A38-86682
# PID 86682 was a zombie/defunct Hermes Electron process

Root Cause Analysis

In apps/desktop/electron/main.ts (line ~11628):

const _gotSingleInstanceLock = app.requestSingleInstanceLock()

if (!_gotSingleInstanceLock) {
  app.quit()
}

Electron's requestSingleInstanceLock() on Linux creates a SingletonLock symlink in the user data dir (~/.config/Hermes/). When the lock exists but points to a dead PID, Electron can still treat it as a valid lock on some X11/Linux setups — requestSingleInstanceLock() returns false, and the app calls app.quit() immediately with no user feedback.

The second-instance handler (line ~11633) never fires because there IS no running first instance — the lock is stale but the process behind it is dead.

The focusWindow function (line ~8645) handles minimized/invisible windows but is never called because the app quits before reaching whenReady().

Proposed Fix

Electron's requestSingleInstanceLock() is known to leave stale locks on Linux when the process dies abnormally. Options:

  1. Before calling app.quit() on lock failure, check if the PID in the SingletonLock symlink target is actually alive. If the PID is dead, remove the stale lock files (SingletonLock, SingletonCookie, SingletonSocket) and retry requestSingleInstanceLock(). This is the approach used by VS Code and other Electron apps.

  2. At startup, proactively validate any existing SingletonLock — if it points to a dead PID, clean it up before attempting to acquire the lock.

  3. Log a message when requestSingleInstanceLock() returns false so the failure is visible in desktop.log (currently completely silent).

Are you willing to submit a PR for this?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/desktopElectron desktop app (apps/desktop/*)type/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions