Several async calls in the message loop and group queue are fire-and-forget without .catch() handlers. When WhatsApp disconnects or containers fail unexpectedly, these produce unhandled rejections that can crash the process. Add explicit .catch() at each call site so errors are logged with full context (groupJid, taskId) instead of crashing: - channel.setTyping() in message loop (adapted for channel abstraction) - startMessageLoop() in main() - runForGroup() and runTask() in group-queue (5 call sites) Closes #221 Co-authored-by: Naveen Jain <1779929+naveenspark@users.noreply.github.com> Co-authored-by: Skip Potter <skip.potter.va@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,9 @@ export class GroupQueue {
|
||||
return;
|
||||
}
|
||||
|
||||
this.runForGroup(groupJid, 'messages');
|
||||
this.runForGroup(groupJid, 'messages').catch((err) =>
|
||||
logger.error({ groupJid, err }, 'Unhandled error in runForGroup'),
|
||||
);
|
||||
}
|
||||
|
||||
enqueueTask(groupJid: string, taskId: string, fn: () => Promise<void>): void {
|
||||
@@ -116,7 +118,9 @@ export class GroupQueue {
|
||||
}
|
||||
|
||||
// Run immediately
|
||||
this.runTask(groupJid, { id: taskId, groupJid, fn });
|
||||
this.runTask(groupJid, { id: taskId, groupJid, fn }).catch((err) =>
|
||||
logger.error({ groupJid, taskId, err }, 'Unhandled error in runTask'),
|
||||
);
|
||||
}
|
||||
|
||||
registerProcess(groupJid: string, proc: ChildProcess, containerName: string, groupFolder?: string): void {
|
||||
@@ -273,13 +277,17 @@ export class GroupQueue {
|
||||
// Tasks first (they won't be re-discovered from SQLite like messages)
|
||||
if (state.pendingTasks.length > 0) {
|
||||
const task = state.pendingTasks.shift()!;
|
||||
this.runTask(groupJid, task);
|
||||
this.runTask(groupJid, task).catch((err) =>
|
||||
logger.error({ groupJid, taskId: task.id, err }, 'Unhandled error in runTask (drain)'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then pending messages
|
||||
if (state.pendingMessages) {
|
||||
this.runForGroup(groupJid, 'drain');
|
||||
this.runForGroup(groupJid, 'drain').catch((err) =>
|
||||
logger.error({ groupJid, err }, 'Unhandled error in runForGroup (drain)'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -298,9 +306,13 @@ export class GroupQueue {
|
||||
// Prioritize tasks over messages
|
||||
if (state.pendingTasks.length > 0) {
|
||||
const task = state.pendingTasks.shift()!;
|
||||
this.runTask(nextJid, task);
|
||||
this.runTask(nextJid, task).catch((err) =>
|
||||
logger.error({ groupJid: nextJid, taskId: task.id, err }, 'Unhandled error in runTask (waiting)'),
|
||||
);
|
||||
} else if (state.pendingMessages) {
|
||||
this.runForGroup(nextJid, 'drain');
|
||||
this.runForGroup(nextJid, 'drain').catch((err) =>
|
||||
logger.error({ groupJid: nextJid, err }, 'Unhandled error in runForGroup (waiting)'),
|
||||
);
|
||||
}
|
||||
// If neither pending, skip this group
|
||||
}
|
||||
|
||||
@@ -370,7 +370,9 @@ async function startMessageLoop(): Promise<void> {
|
||||
messagesToSend[messagesToSend.length - 1].timestamp;
|
||||
saveState();
|
||||
// Show typing indicator while the container processes the piped message
|
||||
channel.setTyping?.(chatJid, true);
|
||||
channel.setTyping?.(chatJid, true)?.catch((err) =>
|
||||
logger.warn({ chatJid, err }, 'Failed to set typing indicator'),
|
||||
);
|
||||
} else {
|
||||
// No active container — enqueue for a new one
|
||||
queue.enqueueMessageCheck(chatJid);
|
||||
@@ -466,7 +468,10 @@ async function main(): Promise<void> {
|
||||
});
|
||||
queue.setProcessMessagesFn(processGroupMessages);
|
||||
recoverPendingMessages();
|
||||
startMessageLoop();
|
||||
startMessageLoop().catch((err) => {
|
||||
logger.fatal({ err }, 'Message loop crashed unexpectedly');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
// Guard: only run when executed directly, not when imported by tests
|
||||
|
||||
Reference in New Issue
Block a user