Fix message cursor to only advance on successful processing (#17)

Previously, lastTimestamp was unconditionally advanced after each message,
even if processMessage failed. This caused transient errors to permanently
drop messages since they would never be retried.

Now the cursor only advances after successful processing, implementing
at-least-once delivery semantics. On failure, the loop breaks and the
failed message will be retried on the next poll iteration.

https://claude.ai/code/session_01SEQDWxXeZHe7t1bb5cw2CA

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-02-01 23:05:37 +02:00
committed by GitHub
parent c45f0efcdb
commit 33ef0c68d3

View File

@@ -522,12 +522,14 @@ async function startMessageLoop(): Promise<void> {
for (const msg of messages) { for (const msg of messages) {
try { try {
await processMessage(msg); await processMessage(msg);
// Only advance timestamp after successful processing for at-least-once delivery
lastTimestamp = msg.timestamp;
saveState();
} catch (err) { } catch (err) {
logger.error({ err, msg: msg.id }, 'Error processing message'); logger.error({ err, msg: msg.id }, 'Error processing message, will retry');
// Stop processing this batch - failed message will be retried next loop
break;
} }
// Advance timestamp after each message to avoid reprocessing on retry
lastTimestamp = msg.timestamp;
saveState();
} }
} catch (err) { } catch (err) {
logger.error({ err }, 'Error in message loop'); logger.error({ err }, 'Error in message loop');