Skip to content

fix(web_server): reap zombie action subprocesses + preserve exit code - #41709

Closed
teknium1 wants to merge 2 commits into
mainfrom
hermes/hermes-b9598731
Closed

fix(web_server): reap zombie action subprocesses + preserve exit code#41709
teknium1 wants to merge 2 commits into
mainfrom
hermes/hermes-b9598731

Conversation

@teknium1

@teknium1 teknium1 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Dashboard action subprocesses (hermes update, skills-update, curator-run, dump, etc.) no longer accumulate as zombies. Salvages #38045 (@liuhao1024) onto current main with a small follow-up.

Root cause: _spawn_hermes_action() stored each detached child's Popen in _ACTION_PROCS and get_action_status() only ever called proc.poll() — never .wait(). A finished child stayed a zombie in the process table until the dashboard server exited (and the stale handle lingered in _ACTION_PROCS).

Changes

  • hermes_cli/web_server.py: on poll() detecting exit, reap via proc.wait(timeout=1) and drop the handle from _ACTION_PROCS (@liuhao1024's fix).
  • hermes_cli/web_server.py: follow-up — migrate the reaped exit code/pid into _ACTION_RESULTS so subsequent status polls keep reporting the real result instead of falling back to None (the dashboard polls repeatedly).
  • tests/hermes_cli/test_web_server.py: reap-and-remove test (@liuhao1024) + second-poll consistency assertion.

Validation

Before After
Finished action child zombie until server exit reaped on next status poll
_ACTION_PROCS after exit stale handle retained handle dropped
2nd status poll after reap exit_code: null, pid: null real exit code/pid preserved

Targeted suite: tests/hermes_cli/test_web_server.py -k "reaped or update_hermes or action_status or get_status" → 6 passed.
E2E: spawned a real detached child, ran the reap logic, confirmed /proc/<pid> has no zombie entry afterward.

Closes #38032. Supersedes #38040 and #38049 (both bundled an unrelated meet_bot pcm_pump change — out of scope for this issue).

Infographic

zombie-reap-dashboard-actions

liuhao1024 and others added 2 commits June 7, 2026 19:44
…accumulation

get_action_status() calls proc.poll() to check if a dashboard action
has finished, but never calls proc.wait() afterward.  On POSIX systems
the kernel retains the process table entry until a blocking waitpid()
is issued, so every completed action remains as a zombie for the
lifetime of the web server.

After poll() returns a non-None exit code, call proc.wait(timeout=1)
to reap the child and remove the handle from _ACTION_PROCS.

Fixes #38032
Follow-up on the zombie-reap fix: once the Popen handle is reaped and
dropped from _ACTION_PROCS, migrate the exit code/pid into _ACTION_RESULTS
so subsequent /api/actions/{name}/status polls keep reporting the real
result instead of falling back to None. The dashboard polls repeatedly,
so without this the status flips from 'exited N' to 'unknown' on the
next poll.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard labels Jun 8, 2026
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-b9598731 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10034 on HEAD, 10033 on base (🆕 +1)

🆕 New issues (1):

Rule Count
invalid-assignment 1
First entries
tests/hermes_cli/test_web_server.py:845: [invalid-assignment] invalid-assignment: Invalid subscript assignment with key of type `Literal["gateway-restart"]` and value of type `FinishedProc` on object of type `dict[str, Popen[Unknown]]`

✅ Fixed issues: none

Unchanged: 5201 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1

teknium1 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing as redundant — #38049 landed on main (78e2101) while this PR was in CI, and its web_server fix is functionally identical (reap on poll + migrate exit code into _ACTION_RESULTS). No need for a second commit on a closed issue.

@teknium1 teknium1 closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: web_server.py: dashboard action subprocesses become zombies — _ACTION_PROCS uses .poll() but never .wait()

3 participants