{"record":{"id":"b32a3336fc6d4639","repo":"nanocoai/nanoclaw","slug":"echo-fan-write-failed","errorCode":null,"errorMessage":"Echo fan write failed","messagePattern":"Echo fan write failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/modules/cross-session-context/fan.ts","lineNumber":172,"sourceCode":"  let written = 0;\n  for (const target of targets) {\n    try {\n      await writeSessionMessage(input.agentGroupId, target.id, {\n        id: echoRowId(input.origMessageId, target.id),\n        kind: 'chat',\n        timestamp: input.timestamp,\n        platformId: input.platformId,\n        channelType: ECHO_CHANNEL_TYPE,\n        threadId: null,\n        content,\n        trigger: false,\n        sourceSessionId: input.sourceSessionId,\n      });\n      written++;\n    } catch (err) {\n      // Per-target isolation: one broken session DB (or a duplicate id from a\n      // replay) must not stop the rest of the fan — or, upstream, routing.\n      log.warn('Echo fan write failed', {\n        targetSessionId: target.id,\n        origMessageId: input.origMessageId,\n        err,\n      });\n    }\n  }\n  return written;\n}\n\n/**\n * Router hook: fan a just-written trigger=1 inbound message into sibling\n * sessions. Call ONLY for the engaged (wake) branch — accumulate (trigger=0)\n * writes must never fan (D3). Never throws; returns rows written.\n */\nexport async function fanInboundMessage(args: {\n  /** Source session the trigger=1 row was written to. */\n  session: Session;\n  /** Messaging group the message arrived on (the source surface). */","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/modules/cross-session-context/fan.ts#L154-L190","documentation":"While fanning an inbound/outbound message to other sessions (cross-session echo), writing to one target session's inbound DB failed. The failure is isolated per-target so remaining targets still receive the message.","triggerScenarios":"A target session's inbound.db is corrupted, locked, or the mount is missing; duplicate message id from a replay violates a uniqueness constraint; disk full on the session data volume.","commonSituations":"Session directory deleted mid-fan; concurrent host writes contending on the SQLite file; replaying routed messages after a restore.","solutions":["Inspect data/v2-sessions/<targetSessionId>/inbound.db for corruption or lock contention","Verify the session directory and mount exist and the host has write access","Re-run or replay the fan for the affected target once the DB is healthy"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"for (const target of targets) {\n  try { await writeEcho(target, input); }\n  catch (err) { log.warn('Echo fan write failed', { target: target.id, err }); }\n}","typeGuard":null,"tryCatchPattern":"catch (err) { /* per-target isolation */ log.warn('Echo fan write failed', { targetSessionId: target.id, err }); }","preventionTips":["Keep one writer per session DB file; avoid external processes touching inbound.db","Alert on repeated fan failures — they indicate a corrupt or locked session DB"],"tags":["cross-session","sqlite","fan-out","partial-failure"],"backgroundTag":"partial-write-failure","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}