{"record":{"id":"aed382d04d96bffb","repo":"moeru-ai/airi","slug":"chat-sync-outbox-drain-failed-for","errorCode":null,"errorMessage":"[chat-sync] outbox drain failed for","messagePattern":"\\[chat-sync\\] outbox drain failed for","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/stores/chat/session-store.ts","lineNumber":1224,"sourceCode":"      for (const [sessionId, sessionEntries] of bySession) {\n        const meta = sessionMetas.value[sessionId]\n        const cloudChatId = meta?.cloudChatId\n        if (!cloudChatId)\n          continue\n        if (!wsClient || wsClient.status() !== 'open')\n          break\n\n        sessionEntries.sort((a, b) => a.queuedAt - b.queuedAt)\n        try {\n          await wsClient.sendMessages({\n            chatId: cloudChatId,\n            messages: sessionEntries.map(e => ({ id: e.messageId, role: e.role, content: e.content })),\n          })\n          succeededIds.push(...sessionEntries.map(e => e.messageId))\n        }\n        catch (err) {\n          const errMsg = errorMessageFrom(err) ?? 'unknown'\n          console.warn('[chat-sync] outbox drain failed for', sessionId, errMsg)\n          for (const entry of sessionEntries) {\n            failedUpdates.push({\n              messageId: entry.messageId,\n              attempts: entry.attempts + 1,\n              lastError: errMsg,\n            })\n          }\n        }\n      }\n\n      if (succeededIds.length > 0)\n        await enqueuePersist(() => chatSessionsRepo.dequeueOutbox(userId, succeededIds))\n      if (failedUpdates.length > 0)\n        await enqueuePersist(() => chatSessionsRepo.updateOutboxEntries(userId, failedUpdates))\n      await refreshOutboxPendingCount()\n    })().finally(() => {\n      outboxDrainTask = undefined\n    })","sourceCodeStart":1206,"sourceCodeEnd":1242,"githubUrl":"https://github.com/moeru-ai/airi/blob/b6d0809ecb6421d492c4ce38aa7a76146c83269d/packages/stage-ui/src/stores/chat/session-store.ts#L1206-L1242","documentation":"The batched outbox drain failed to deliver all entries of one session in a single sendMessages call. Every entry in that failing session batch gets attempts + 1 and the shared lastError, while other sessions that succeeded are dequeued via dequeueOutbox(succeededIds). One poison message fails its whole session batch; the drain is single-flight guarded and safe to retrigger.","triggerScenarios":"Batch drain over a flaky connection; one malformed or oversized message in a session batch failing the entire sendMessages call; chat deleted remotely before the backlog drained.","commonSituations":"Startup drain of an offline backlog; resuming after a long sleep; a single invalid message repeatedly blocking its session's batch.","solutions":["Restore connectivity and let the single-flight drain retry on its next trigger","Find the poison message: entries with growing attempts/lastError in the outbox repo identify it","If one message always fails its session batch, fix or remove it (e.g. re-mint the chatId) to unblock the rest","When the whole batch keeps failing with not-found, verify the chat still exists server-side"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if (sessionEntries.length === 0) continue // nothing to send for this session\nif (!cloudChatId) continue // no mapping yet","typeGuard":null,"tryCatchPattern":"try {\n  await wsClient.sendMessages({ chatId: cloudChatId, messages: sessionEntries.map(e => ({ id: e.messageId, role: e.role, content: e.content })) })\n  succeededIds.push(...sessionEntries.map(e => e.messageId))\n}\ncatch (err) {\n  // fail only this session's batch; other sessions continue draining\n  for (const entry of sessionEntries)\n    failedUpdates.push({ messageId: entry.messageId, attempts: entry.attempts + 1, lastError: errorMessageFrom(err) ?? 'unknown' })\n}","preventionTips":["Batch per session so one bad session never blocks others (source already does this)","Cap attempts and dead-letter entries that always fail to unblock the rest","Inspect per-entry lastError to distinguish server rejections from network drops"],"tags":["chat-sync","outbox","batch-send","retry"],"backgroundTag":"outbox-delivery-failed","analyzedSha":"b6d0809ecb6421d492c4ce38aa7a76146c83269d","analyzedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-08-23T11:17:13.642Z"}