thedotmack/claude-mem · warning

cloud sync identity unavailable; refusing an unreplicated de

Error message

cloud sync identity unavailable; refusing an unreplicated delete

What it means

HTTP 503 returned by the DataRoutes delete handler when cloud sync IS configured but has no deviceId yet — the device identity that tags replicated mutations has not been established. Deleting a local-origin row without that identity would emit a delete other replicas could not attribute, so the worker refuses and asks the client to retry later.

Source

Thrown at src/services/worker/http/routes/DataRoutes.ts:361

      this.badRequest(res, 'id must be a positive canonical decimal string');
      return;
    }

    const store = this.dbManager.getSessionStore();
    const row = store.db.prepare(`
      SELECT CAST(id AS TEXT) AS id FROM ${table}
      WHERE id = ? AND origin_device_id IS NULL
    `).get(originLocalId) as { id: string } | undefined;
    if (!row) {
      this.notFound(res, `${kind} #${originLocalId} not found`);
      return;
    }

    const cloudSync = this.dbManager.getCloudSync();
    let entityRev: string | null = null;
    if (cloudSync?.isConfigured()) {
      if (!cloudSync.status().deviceId) {
        res.status(503).json({ error: 'cloud sync identity unavailable; refusing an unreplicated delete' });
        return;
      }
      entityRev = cloudSync.queueDelete(kind, originLocalId);
    } else {
      // A row with an acknowledged entity head must never be silently deleted
      // while its sync identity is unavailable: that would strand replicas.
      const acknowledged = store.db.prepare(`
        SELECT 1 AS found FROM sync_entity_heads
        WHERE kind = ? AND origin_local_id = ? LIMIT 1
      `).get(kind, originLocalId) as { found: number } | undefined;
      if (acknowledged) {
        res.status(503).json({ error: 'cloud sync unavailable; refusing an unreplicated delete' });
        return;
      }
      store.db.prepare(
        `DELETE FROM ${table} WHERE id = ? AND origin_device_id IS NULL`
      ).run(originLocalId);
    }

View on GitHub (pinned to 8bc631a71a)

Solutions

  1. Retry the delete after the first successful sync run establishes the deviceId
  2. Check sync status (cloudSync.status()) — if deviceId stays null, inspect sync credentials/connectivity
  3. If sync was enabled by mistake, disable cloud sync so the delete takes the local-only path

Example fix

// before
await fetch(`${base}/api/data/session/${id}`, { method: 'DELETE' }); // 503 identity unavailable

// after
await waitForSyncIdentity(); // poll status endpoint until deviceId present
await fetch(`${base}/api/data/session/${id}`, { method: 'DELETE' });
Defensive patterns

Strategy: retry

Validate before calling

async function syncIdentityReady(base: string): Promise<boolean> {
  // before deleting replicated data, confirm the device identity exists
  const s = await (await fetch(`${base}/api/settings`)).json();
  return Boolean(s.cloudSync?.deviceId);
}

Type guard

function isSyncIdentityUnavailable(body: unknown, status: number): boolean {
  return status === 503 && typeof body === 'object' && body !== null &&
    String((body as { error?: string }).error).includes('identity unavailable');
}

Try / catch

for (let i = 0; i < 5; i++) {
  const res = await del(`${base}/api/data/${kind}/${id}`);
  if (res.status !== 503) return handle(res);
  await sleep(2000); // first sync exchange provisions deviceId
}
throw new Error('sync identity never became available');

Prevention

When it happens

Trigger: DELETE on a local-origin session/observation immediately after configuring cloud sync, before the first successful sync exchange provisions the device identity; or after an identity reset where status().deviceId is null again.

Common situations: Fresh installs where sync credentials were entered but the initial handshake failed (network down, bad endpoint); race between the settings API enabling sync and the sync engine registering; wiping local identity state under ~/.claude-mem.

Related errors


AI-assisted analysis of thedotmack/claude-mem@8bc631a71a (2026-08-20). Data as JSON: /api/errors/71f577c2f556a7b4. Report an issue: GitHub.