fix(session): don't resurrect a session stopped/deleted during start() - #405
Merged
Conversation
If a stop()/delete() landed while start() was awaiting the engine's initialize(), the freshly-created engine was left registered and READY — the session came back up even though it was meant to be down. start() now re-checks stoppingSessions after initializeEngine() and, if set, tears down the just-registered engine and removes it (mirroring the proven post-init guard in executeReconnect). The narrower async-callback window is intentionally left as-is, matching the existing reconnect path, to avoid the higher-risk callback-identity change.
Merged
mmr94
added a commit
to mmr94/unisoft-OpenWA
that referenced
this pull request
Jun 24, 2026
Le fork avait 315 commits / 6 versions de retard. Ce merge corrige la cause racine d'un bug d'envoi de média WhatsApp : MessageMedia.fromUrl() sans timeout (fetch interne + DNS non borné) bloquait Chromium jusqu'au protocolTimeout, vu côté Kehila comme un NETWORK_ERROR en boucle. Upstream remplace ce chemin par loadRemoteMedia() (fetch borné + SSRF-gardé, rmyndharis#404) et ajoute POST /:id/force-kill pour tuer un moteur Chromium bloqué. Conflits résolus en préservant la feature maison d'hibernation de sessions ET les fixes de fiabilité upstream (rmyndharis#404 SSRF/DNS, rmyndharis#405 no-resurrect, rmyndharis#410 superseded engine, rmyndharis#415 reconcile-ready) : - session.service.ts (9 zones) : intentionalStops + stoppingSessions combinés dans onDisconnected ; hibernate/wake/ensureEngineReady/markActivity cohabitent avec forceKill ; cleanup hibernation greffé dans onModuleDestroy parallèle. - session.controller.ts : endpoints wake + force-kill ; transformSession délègue à SessionResponseDto.fromEntity (mapper complété avec lastSent). - bulk-message.service.ts : markActivity + persistSentMessage conservés. - events.gateway / spec / docs : fusion des deux côtés. Corrections post-merge : registre HOOK_EVENT_REGISTRY complété (events hibernation), await manquant sur getEngine dans getChatHistory, mocks ensureEngineReady/markActivity dans bulk-message.spec. Vérifié : nest build OK ; session.service.spec 105/105 ; bulk-message.spec 15/15. (Échecs sqlite3 résiduels = binding natif absent en local, sans rapport avec le code.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
start()reserves its slot, clears the stop flag, thenawaitsinitializeEngine()(which registers the engine and runsengine.initialize()). If astop()ordelete()lands during that await, it sets the stopping flag and tears down — butstart()did not re-check afterward, so the freshly-created engine was left registered and driven toREADY. The session came back up even though it was meant to be down (and could orphan a Chromium against a deleted session row).start()now mirrors the proven post-init guard already used byexecuteReconnect(): afterinitializeEngine(), if the session is marked stopping, tear down the just-registered engine viateardownEngineSafelyand remove it before returning. A concurrent stop/delete wins.Scope note: the narrower window where
initialize()'s async callbacks fire afterstart()returns is intentionally left unchanged — that matches the existingexecuteReconnectpath, and the alternative (an engine-identity check inside the callbacks) is higher-risk (it could suppress a legitimate status write during a normal reconnect). This PR takes only the low-risk, proven guard.Tests
session.service.spec.ts: a stop/delete simulated duringinitialize()now results in the engine beingdestroy()ed and removed (getEngineundefined) rather than left READY.Risk
Low. The guard mirrors logic already in production on the reconnect path; it only fires when a stop/delete genuinely raced the start, and the safe outcome is "the session stays down" (what the operator asked for).