Skip to content

feat(session): force-kill a stuck session (engine forceDestroy + POST /sessions/:id/force-kill)#352

Closed
rmyndharis wants to merge 1 commit into
mainfrom
feat/force-kill-stuck-session
Closed

feat(session): force-kill a stuck session (engine forceDestroy + POST /sessions/:id/force-kill)#352
rmyndharis wants to merge 1 commit into
mainfrom
feat/force-kill-stuck-session

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Backend for the "Kill Stuck" dashboard button (#351). #351's button calls POST /api/sessions/:id/force-kill, but that endpoint did not exist on main (the original backend PR, #316/#329, was closed with an empty branch). This adds it the engine-neutral way.

Why not pkill chromium

The earlier attempt used a process-level pkill, which is wrong for an engine-abstracted layer: it's Puppeteer-specific (meaningless for Baileys) and would kill other sessions' (or the system's) Chromium, not just the stuck one. Instead this puts the responsibility on the engine — each adapter kills only its own resources.

What this adds

  • IWhatsAppEngine.forceDestroy() — force-kill the engine's own resources, then best-effort graceful teardown.
    • whatsapp-web.js: SIGKILLs this client's own Chromium process via client.pupBrowser.process(), then best-effort client.destroy(). A missing process handle or a hung destroy() can't prevent teardown.
    • Baileys: ends its own socket (no separate browser process), so forceDestroy() is just a destroy().
  • SessionService.forceKill(id) — mirrors stop()'s lifecycle: sets the stop-mark (blocks an in-flight reconnect from resurrecting the session it just killed; a later start() clears it), cancels pending reconnects, and runs the time-bounded, isolated teardown (teardownEngineSafely, 10s cap) so a still-wedged engine can't hang the call. The Map is reconciled and the session is left DISCONNECTED and restartable.
  • POST /sessions/:id/force-kill (OPERATOR, mirrors stop) → 200 with the session, 404 if unknown. Audited as session_force_killed.

No migration (the audit-action column is a free varchar), no new dependency, no change to existing routes.

Tests

  • whatsapp-web.js forceDestroy: SIGKILLs only its own browser process then destroys; still completes when the process handle is gone and destroy() rejects; no-op with no client.
  • forceKill: force-destroys + reconciles the Map + keeps the stop-mark (so a late reconnect can't revive it); completes even when forceDestroy() rejects; 404 for an unknown session.

Full backend 734/734; dashboard build + lint clean.

Merge note

Merge this before #351 so the dashboard button has a working endpoint. The two together close the stuck-session-recovery gap.

… /sessions/:id/force-kill)

Adds an engine-neutral forceDestroy() to IWhatsAppEngine and a POST /sessions/:id/force-kill route (OPERATOR) to recover a session whose engine is wedged and won't respond to a normal stop()/delete().

- whatsapp-web.js: SIGKILLs THIS client's own Chromium process directly (client.pupBrowser.process()), never a process-wide pkill that could take down other sessions, then best-effort client.destroy().

- Baileys: ends its own socket (no separate browser process).

- session.forceKill() mirrors stop()'s lifecycle (stop-mark + cancel-reconnect + time-bounded, isolated teardown + Map reconciliation), leaving the session DISCONNECTED and restartable.

Backs the dashboard Kill Stuck button (#351), whose backend dependency was missing from main.
rmyndharis added a commit that referenced this pull request Jun 19, 2026
* fix(webhook): deliver session lifecycle events and key webhook hardening (#335)

* fix(security): pin outbound webhook and media fetches to validated IP (#338)

* fix(plugins): persist plugin enable/config and restore (#339)

* fix(message): persist bulk-sent messages, sanitize SSRF (#340)

* fix(engine): return the real id for forwarded messages (#341)

* fix(security): harden outbound requests, IPv6 SSRF (#344)

* fix(security): secret-file perms, key pepper, allowedIps (#345)

* fix(storage): bound tar imports, contain storage keys (#346)

* fix(session): reconcile late acks, serialize reactions (#348)

* fix(contract): webhook timeout, bounded shutdown, 501 (#350)

* feat(session): force-kill a stuck session (#352)

* merge #343

* merge #351

* chore(release): v0.4.3 — CHANGELOG, version bump (package.json/dashboard/swagger), README + docs
@rmyndharis

Copy link
Copy Markdown
Owner Author

Shipped in v0.4.3 (integrated via the release PR #354 and tagged v0.4.3). Thanks! 🎉

@rmyndharis rmyndharis closed this Jun 19, 2026
@rmyndharis rmyndharis deleted the feat/force-kill-stuck-session branch June 19, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant