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 tick

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Inspect the message content for something that crashes the agent (huge attachment, weird format) — a poison message
  2. Fix the underlying per-attempt failure (container crash, provider error) seen in logs before the retries exhausted
  3. Manually reset the message's failed status / tries in inbound.db to retry once fixed
  4. 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

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.