nanocoai/nanoclaw · warning

Delivery batch-preview hook failed

Error message

Delivery batch-preview hook failed

What it means

A registered delivery batch-preview hook threw while previewing a batch of pending outbound messages. The preview is advisory, so delivery proceeds regardless; only the hook's side effect (e.g. dashboards, metrics) is skipped for this batch.

Solutions

  1. Identify which registered hook threw from the error detail
  2. Fix or disable the offending add-on (e.g. dashboard pusher config)
  3. Restart the host after fixing
Defensive patterns

Strategy: try-catch

Try / catch

try { await hook(batch, session); } catch (err) { log.warn(...); /* delivery proceeds */ }

Prevention

When it happens

Trigger: A hook registered via the batch-preview hook list throws — commonly a pusher with a dead HTTP endpoint or a serialization error on message content.

Common situations: Dashboard/metrics add-on skills whose pusher endpoint is down or whose payload shape changed after an update.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/a686f26d5788380a. Report an issue: GitHub.

Appendix: source

Thrown at src/delivery.ts:214

    if (!existing) return;
    ({ delivered, pending } = existing);
  } catch (err) {
    log.error('Session mailbox delivery failed', {
      agentGroupId: agentGroup.id,
      sessionId: session.id,
      err,
    });
    return;
  }

  for (const hook of batchPreviewHooks) {
    try {
      await hook(
        pending.map(({ kind, content }) => ({ kind, content })),
        session,
      );
    } catch (err) {
      log.warn('Delivery batch-preview hook failed', { sessionId: session.id, err });
    }
  }

  for (const msg of pending) {
    try {
      const platformMsgId = await deliverMessage(msg, session);
      await withExistingMailboxSession(agentGroup.id, session.id, (mailbox) =>
        mailbox.markDelivered(msg.id, platformMsgId ?? null),
      );
      const firstDelivery = delivered.size === 0;
      delivered.add(msg.id);
      deliveryAttempts.delete(msg.id);
      if (msg.kind !== 'system' && msg.channelType !== 'agent') {
        pauseTypingRefreshAfterDelivery(session.id);
        if (msg.kind !== 'task_log') {
          await fanOutboundMessage(
            {
              id: msg.id,

View on GitHub (pinned to 294ef2aee8)