{"record":{"id":"a686f26d5788380a","repo":"nanocoai/nanoclaw","slug":"delivery-batch-preview-hook-failed","errorCode":null,"errorMessage":"Delivery batch-preview hook failed","messagePattern":"Delivery batch-preview hook failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/delivery.ts","lineNumber":214,"sourceCode":"    if (!existing) return;\n    ({ delivered, pending } = existing);\n  } catch (err) {\n    log.error('Session mailbox delivery failed', {\n      agentGroupId: agentGroup.id,\n      sessionId: session.id,\n      err,\n    });\n    return;\n  }\n\n  for (const hook of batchPreviewHooks) {\n    try {\n      await hook(\n        pending.map(({ kind, content }) => ({ kind, content })),\n        session,\n      );\n    } catch (err) {\n      log.warn('Delivery batch-preview hook failed', { sessionId: session.id, err });\n    }\n  }\n\n  for (const msg of pending) {\n    try {\n      const platformMsgId = await deliverMessage(msg, session);\n      await withExistingMailboxSession(agentGroup.id, session.id, (mailbox) =>\n        mailbox.markDelivered(msg.id, platformMsgId ?? null),\n      );\n      const firstDelivery = delivered.size === 0;\n      delivered.add(msg.id);\n      deliveryAttempts.delete(msg.id);\n      if (msg.kind !== 'system' && msg.channelType !== 'agent') {\n        pauseTypingRefreshAfterDelivery(session.id);\n        if (msg.kind !== 'task_log') {\n          await fanOutboundMessage(\n            {\n              id: msg.id,","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/delivery.ts#L196-L232","documentation":"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.","triggerScenarios":"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.","commonSituations":"Dashboard/metrics add-on skills whose pusher endpoint is down or whose payload shape changed after an update.","solutions":["Identify which registered hook threw from the error detail","Fix or disable the offending add-on (e.g. dashboard pusher config)","Restart the host after fixing"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await hook(batch, session); } catch (err) { log.warn(...); /* delivery proceeds */ }","preventionTips":["Hooks must be side-effect-only and never throw","Health-check pusher endpoints inside add-ons","Disable add-on hooks when their backend is down"],"tags":["delivery","hooks","extensibility"],"backgroundTag":"hook-callback-threw","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}