thedotmack/claude-mem · error · Error

cloud sync must be configured before queueDelete

Error message

cloud sync must be configured before queueDelete

What it means

CloudSync.queueDelete refuses to run when the sync engine is not in an active, fully configured state (isActive() is false). Deleting content is a two-phase operation — a durable tombstone written to sync_content_outbox followed by hub projection — so it requires a configured cloud sync connection. The library fails closed rather than silently dropping a deletion that would never propagate.

Solutions

  1. Ensure cloud sync is configured (hub URL, token, device id) and activated before any queueDelete call.
  2. Check CLAUDE_MEM_CLOUD_SYNC_HUB_URL and related settings in ~/.claude-mem/settings.json or ~/.claude-mem/.env.
  3. Gate deletion UI/logic on the sync engine's active/ready state instead of calling blindly.
  4. If sync should run, reinitialize or restart the CloudSync instance so isActive() returns true.

Example fix

// before
sync.queueDelete('observation', localId);
// after
if (!sync.isActive()) await sync.configureAndStart();
sync.queueDelete('observation', localId);
Defensive patterns

Strategy: validation

Validate before calling

if (sync.isActive()) sync.queueDelete(kind, originLocalId);

Try / catch

try { sync.queueDelete(kind, id); } catch (e) { if (e.message.includes('must be configured')) await sync.configureAndStart(); }

Prevention

When it happens

Trigger: Calling queueDelete(kind, originLocalId, deletedAt?) on a CloudSync instance before configure()/activation has completed, or after sync was deactivated/torn down.

Common situations: App code invokes deletion helpers at startup before the sync config (hub URL, token, device id) is loaded; sync is disabled by missing env vars like CLAUDE_MEM_CLOUD_SYNC_HUB_URL; the sync worker was stopped but a deletion path still fires.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/21d8689f3bbb7a83. Report an issue: GitHub.

Appendix: source

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

      const raw = error instanceof Error ? error.message : String(error);
      const safe = this.token === '' ? raw : raw.split(this.token).join('[REDACTED]');
      this.hubStatus = {
        checkedAt,
        reachable: false,
        epoch: null,
        headSeq: null,
        projectedSeq: null,
        error: safe,
      };
    }
  }

  /**
   * Queue and apply a local content deletion atomically. The tombstone stays
   * durable in sync_content_outbox until Hub projection acknowledges it.
   */
  queueDelete(kind: RowKind, originLocalId: string, deletedAt = new Date().toISOString()): string {
    if (!this.isActive()) throw new Error('cloud sync must be configured before queueDelete');
    assertCanonicalDecimal(originLocalId);
    const entityId = stableDocumentId(kind, this.deviceId, originLocalId);
    const table = TABLE_BY_KIND[kind];
    let entityRev = '0';
    const tx = this.db.transaction(() => {
      const revisions: string[] = [];
      const local = this.db.prepare(
        `SELECT CAST(sync_rev AS TEXT) AS entity_rev
         FROM ${table} WHERE id = ? AND origin_device_id IS NULL`
      ).get(originLocalId) as { entity_rev: string } | undefined;
      if (local) revisions.push(local.entity_rev);
      const head = this.db.prepare(
        'SELECT entity_rev FROM sync_entity_heads WHERE entity_id = ?'
      ).get(entityId) as { entity_rev: string } | undefined;
      if (head) revisions.push(head.entity_rev);
      const pending = this.db.prepare(
        'SELECT entity_rev FROM sync_content_outbox WHERE entity_id = ?'
      ).all(entityId) as Array<{ entity_rev: string }>;

View on GitHub (pinned to d8bc9755e7)