nanocoai/nanoclaw · warning
Message marked as failed after max retries
Error message
Message marked as failed after max retries
What it means
A message that kept getting stuck and retried has exhausted MAX_TRIES, so the sweep marks it failed in inbound.db instead of re-queueing again. After this warn, the message will not be delivered — the agent never processes it unless manually requeued.
Source
Thrown at src/host-sweep.ts:324
inDb: InboundMailbox,
outDb: OutboundMailbox,
session: Session,
reason: string,
): void {
const claims = outDb.getProcessingClaims();
const now = Date.now();
for (const { messageId } of claims) {
const msg = inDb.getMessageForRetry(messageId, 'pending');
if (!msg) continue;
// Already rescheduled for a future retry — don't bump tries again. The
// wake path (sweep step 2) will fire when process_after elapses and a
// fresh container will clean the orphan claim on startup.
if (msg.processAfter && Date.parse(msg.processAfter) > now) continue;
if (msg.tries >= MAX_TRIES) {
inDb.markMessageFailed(msg.id);
log.warn('Message marked as failed after max retries', {
messageId: msg.id,
sessionId: session.id,
reason,
});
} else {
const backoffMs = BACKOFF_BASE_MS * Math.pow(2, msg.tries);
const backoffSec = Math.floor(backoffMs / 1000);
inDb.retryWithBackoff(msg.id, backoffSec);
log.info('Reset stale message with backoff', {
messageId: msg.id,
tries: msg.tries,
backoffMs,
reason,
});
}
}
// Drop the orphan 'processing' rows. Without this, the next sweep tickView on GitHub (pinned to 294ef2aee8)
Solutions
- Inspect the message content for something that crashes the agent (huge attachment, weird format) — a poison message
- Fix the underlying per-attempt failure (container crash, provider error) seen in logs before the retries exhausted
- Manually reset the message's failed status / tries in inbound.db to retry once fixed
- If failures are environmental flakiness, raise MAX_TRIES or backoff tuning
Example fix
-- retry a failed message once root cause is fixed (session inbound.db) UPDATE messages_in SET status='pending', tries=0 WHERE id='<messageId>';
Defensive patterns
Strategy: validation
Validate before calling
// inbound.db check before it hits MAX_TRIES
const tries = await inDb.getMessageTries(msgId);
if (tries >= MAX_TRIES - 1) {
// pull the message aside for manual inspection instead of letting it fail silently
} Prevention
- Alert on retry counts climbing toward MAX_TRIES
- Quarantine messages that repeatedly crash the agent rather than letting them loop
- Fix per-attempt root causes (auth, crashes) quickly — retries burn fast under backoff
When it happens
Trigger: resetStuckProcessingRows sees msg.tries >= MAX_TRIES for a stuck/claimed message whose process_after has elapsed; markMessageFailed(msg.id) is called with the reset reason (e.g. 'claim-stuck', 'absolute-ceiling').
Common situations: Repeated container crashes on the same message (poison message); provider auth failure causing every retry to fail; persistent container instability making each attempt die mid-processing.
Related errors
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/e2fdc11ac7f52c12.
Report an issue: GitHub.