{"record":{"id":"2ee64570594238c7","repo":"thedotmack/claude-mem","slug":"cloud-sync-flush-failed-unsynced-rows-remain-queu","errorCode":null,"errorMessage":"Cloud sync flush failed; unsynced rows remain queued","messagePattern":"Cloud sync flush failed; unsynced rows remain queued","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/sync/CloudSync.ts","lineNumber":538,"sourceCode":"    this.flushing = true;\n    try {\n      do {\n        this.flushAgainRequested = false;\n        await this.drainContentOutbox();\n        await this.drainMutations();\n        for (const kind of KINDS) {\n          await this.drainKind(kind);\n        }\n      } while (this.flushAgainRequested && !this.stopped);\n      if (this.stopped) return; // shutdown mid-flush — skip success bookkeeping\n      this.lastFlushAt = Date.now();\n      this.lastError = null;\n      this.resetBackoff();\n    } catch (error) {\n      const err = error instanceof Error ? error : new Error(String(error));\n      this.lastError = err.message;\n      // Rows stay NULL — retried by the backoff timer below and on next notify().\n      logger.warn('CLOUD_SYNC', 'Cloud sync flush failed; unsynced rows remain queued', {\n        retryInMs: this.nextBackoffMs,\n      }, err);\n      this.scheduleRetry();\n    } finally {\n      this.flushing = false;\n    }\n  }\n\n  status(): CloudSyncStatus {\n    return {\n      configured: this.isConfigured(),\n      deviceId: this.deviceId,\n      pending: {\n        observations: this.countPending('observations'),\n        summaries: this.countPending('session_summaries'),\n        prompts: this.countPending('user_prompts'),\n        mutations: this.countPendingMutations(),\n        tombstones: this.countPendingTombstones(),","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/CloudSync.ts#L520-L556","documentation":"The cloud-sync flush loop drains every kind in sequence; any throw is caught here — lastError is recorded, unsynced rows keep their NULL markers so they stay queued, the warning carries the next backoff delay, and scheduleRetry() re-arms the timer. A shutdown detected mid-flush skips the bookkeeping entirely.","triggerScenarios":"drainKind throws for any kind — hub 5xx, auth rejection, or network drop — during a flush pass.","commonSituations":"Expired sync credentials, sync hub outage, or the machine switching networks mid-flush.","solutions":["Check connectivity and credentials for the sync hub; queued rows retry automatically on the backoff timer.","Re-run the sync configuration if the token expired.","Inspect status() (lastError) afterwards to confirm the flush recovered."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  for (const kind of KINDS) await drainKind(kind);\n} catch (err) {\n  // rows keep NULL sync markers; re-arm the backoff timer and retry later\n  scheduleRetry();\n}","preventionTips":["Keep sync credentials fresh; reconfigure before expiry.","Expect eventual delivery — rows queue and retry on backoff, they are not dropped."],"tags":["cloud-sync","flush","backoff","queued-retry"],"backgroundTag":"sync-flush-retry","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}