Skip to content

Commit c29bcc5

Browse files
inovacaoecrescimentoBruno Sgarbi
andauthored
fix(instance.controller): emit remove.instance even when logout fails (zombie cleanup) (#2520)
* fix(instance.controller): emit remove.instance even when logout fails When a Baileys WebSocket dies but the in-memory `waInstances[name]` entry still exists (a "zombie" instance), `deleteInstance()` calls `await this.logout()` which throws "Connection Closed". The throw causes the outer try/catch to skip the `eventEmitter.emit('remove.instance')` call — which is the only mechanism that purges the zombie from `waInstances`. Result: zombies persist in memory until the entire `evo2_api` container is restarted, affecting ALL instances on the host (not just the broken one). Operators have no per-instance recovery path in v2.3.x — their only option is `docker restart`, which forces every connected user to re-scan the QR code. Fix: wrap the inner `logout()` call in its own try/catch. Log a warning when it fails but continue to the cleanup emit. The in-memory entry must be removed regardless of whether logout completed cleanly — `remove.instance` is the canonical way to purge a stuck instance, and DB/cache cleanup happens in the same event handler. This makes `DELETE /instance/:name` idempotent against zombies: a caller can always recover a single instance without nuking the whole host. Refs: - #693 (instance/restart closes the session) - #1286 (Connection Closed in v2.2.3) - #2026 (Sync lost after reboot) - #2027 (Loss of synchronization on reboot) Tested in production at Rigarr (14 instances, ~25k msgs/day) by overlaying this patch on v2.3.7 via Docker. Before: any zombie forced a full container restart. After: per-instance cleanup works cleanly while other vendors stay connected. Signed-off-by: Bruno Cavalcante Sgarbi <bcsgarbi@gmail.com> * review: address Sourcery feedback — neutral language + log error object Per #2520 review: 1. Drop vendor-specific markers in code comment and log message (was '[ZOMBIE-CLEANUP]' and 'RIGARR PATCH'). Comment now describes the bug in upstream-friendly terms. 2. Pass the full error object to logger.warn instead of toString(), following the existing convention in monitor.service.ts ('no.connection' handler) where structured object logging is used to preserve diagnostic detail. No behavior change. --------- Signed-off-by: Bruno Cavalcante Sgarbi <bcsgarbi@gmail.com> Co-authored-by: Bruno Sgarbi <bcsgarbi@gmail.com>
1 parent 3f567db commit c29bcc5

1 file changed

Lines changed: 15 additions & 1 deletion

File tree

src/api/controllers/instance.controller.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,21 @@ export class InstanceController {
458458
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED) waInstances?.clearCacheChatwoot();
459459

460460
if (instance.state === 'connecting' || instance.state === 'open') {
461-
await this.logout({ instanceName });
461+
try {
462+
await this.logout({ instanceName });
463+
} catch (error) {
464+
// logout can throw "Connection Closed" when the underlying Baileys
465+
// socket is already dead but waInstances[name] still exists. We
466+
// must continue to the remove.instance emit below — that is the
467+
// only path that purges the in-memory entry and runs cleaningUp().
468+
// Without this catch, the stale entry persists until the entire
469+
// process restarts.
470+
this.logger.warn({
471+
message: 'logout failed during deleteInstance — proceeding with cleanup',
472+
instanceName,
473+
error,
474+
});
475+
}
462476
}
463477

464478
try {

0 commit comments

Comments
 (0)