thedotmack/claude-mem · warning

Cloud sync flush failed; unsynced rows remain queued

Error message

Cloud sync flush failed; unsynced rows remain queued

What it means

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.

Source

Thrown at src/services/sync/CloudSync.ts:538

    this.flushing = true;
    try {
      do {
        this.flushAgainRequested = false;
        await this.drainContentOutbox();
        await this.drainMutations();
        for (const kind of KINDS) {
          await this.drainKind(kind);
        }
      } while (this.flushAgainRequested && !this.stopped);
      if (this.stopped) return; // shutdown mid-flush — skip success bookkeeping
      this.lastFlushAt = Date.now();
      this.lastError = null;
      this.resetBackoff();
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.lastError = err.message;
      // Rows stay NULL — retried by the backoff timer below and on next notify().
      logger.warn('CLOUD_SYNC', 'Cloud sync flush failed; unsynced rows remain queued', {
        retryInMs: this.nextBackoffMs,
      }, err);
      this.scheduleRetry();
    } finally {
      this.flushing = false;
    }
  }

  status(): CloudSyncStatus {
    return {
      configured: this.isConfigured(),
      deviceId: this.deviceId,
      pending: {
        observations: this.countPending('observations'),
        summaries: this.countPending('session_summaries'),
        prompts: this.countPending('user_prompts'),
        mutations: this.countPendingMutations(),
        tombstones: this.countPendingTombstones(),

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Check connectivity and credentials for the sync hub; queued rows retry automatically on the backoff timer.
  2. Re-run the sync configuration if the token expired.
  3. Inspect status() (lastError) afterwards to confirm the flush recovered.
Defensive patterns

Strategy: retry

Try / catch

try {
  for (const kind of KINDS) await drainKind(kind);
} catch (err) {
  // rows keep NULL sync markers; re-arm the backoff timer and retry later
  scheduleRetry();
}

Prevention

When it happens

Trigger: drainKind throws for any kind — hub 5xx, auth rejection, or network drop — during a flush pass.

Common situations: Expired sync credentials, sync hub outage, or the machine switching networks mid-flush.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/2ee64570594238c7. Report an issue: GitHub.