{"record":{"id":"7962282ab6a60f38","repo":"thedotmack/claude-mem","slug":"cloud-sync-unavailable-refusing-an-unreplicated-d","errorCode":null,"errorMessage":"cloud sync unavailable; refusing an unreplicated delete","messagePattern":"cloud sync unavailable; refusing an unreplicated delete","errorType":"http","errorClass":null,"httpStatus":503,"severity":"warning","filePath":"src/services/worker/http/routes/DataRoutes.ts","lineNumber":373,"sourceCode":"    }\n\n    const cloudSync = this.dbManager.getCloudSync();\n    let entityRev: string | null = null;\n    if (cloudSync?.isConfigured()) {\n      if (!cloudSync.status().deviceId) {\n        res.status(503).json({ error: 'cloud sync identity unavailable; refusing an unreplicated delete' });\n        return;\n      }\n      entityRev = cloudSync.queueDelete(kind, originLocalId);\n    } else {\n      // A row with an acknowledged entity head must never be silently deleted\n      // while its sync identity is unavailable: that would strand replicas.\n      const acknowledged = store.db.prepare(`\n        SELECT 1 AS found FROM sync_entity_heads\n        WHERE kind = ? AND origin_local_id = ? LIMIT 1\n      `).get(kind, originLocalId) as { found: number } | undefined;\n      if (acknowledged) {\n        res.status(503).json({ error: 'cloud sync unavailable; refusing an unreplicated delete' });\n        return;\n      }\n      store.db.prepare(\n        `DELETE FROM ${table} WHERE id = ? AND origin_device_id IS NULL`\n      ).run(originLocalId);\n    }\n\n    res.json({ success: true, id: originLocalId, kind, entity_rev: entityRev });\n  }\n\n  private handleGetStats = this.wrapHandler((req: Request, res: Response): void => {\n    const db = this.dbManager.getSessionStore().db;\n\n    const packageRoot = getPackageRoot();\n    const packageJsonPath = path.join(packageRoot, 'package.json');\n    const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n    const version = packageJson.version;\n","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/services/worker/http/routes/DataRoutes.ts#L355-L391","documentation":"HTTP 503 from the same DataRoutes delete handler, on the opposite branch: cloud sync is NOT configured, but the row has an entry in sync_entity_heads — proof another device has already acknowledged it. Deleting it now would strand replicas that still reference the entity, so the worker refuses the unreplicated delete. This is a data-integrity guard, not a transient error.","triggerScenarios":"DELETE on a local-origin row that previously replicated (an entity head exists) after cloud sync was disabled, deconfigured, or its credentials expired; deleting data on a secondary machine whose sync setup was removed but whose database still carries acknowledged heads.","commonSituations":"User turns cloud sync off to 'go local' then tries to prune old sessions; sync token revoked server-side so isConfigured() is false while sync_entity_heads still holds rows; switching a machine between sync accounts.","solutions":["Reconfigure cloud sync so the delete replicates through queueDelete (the intended path)","If going local-only permanently is deliberate, clear the stale sync_entity_heads rows for that entity or wipe the sync metadata — accepting other replicas keep their copy","Verify you actually want the delete replicated: the guard exists to prevent silent divergence"],"exampleFix":"-- before: delete refused with 503 because entity head exists\nDELETE /api/data/session/42\n\n-- after: re-enable sync first, then delete replicates\n-- (worker settings) cloudSync.configure({...})\nDELETE /api/data/session/42  --> { success: true, entity_rev: \"...\" }","handlingStrategy":"retry","validationCode":"async function deleteWillReplicate(base: string, kind: string, id: number): Promise<boolean> {\n  // rough client-side check: sync configured OR no acknowledged heads\n  const s = await (await fetch(`${base}/api/settings`)).json();\n  return Boolean(s.cloudSync?.configured);\n}","typeGuard":"function isUnreplicatedDeleteRefused(body: unknown, status: number): boolean {\n  return status === 503 && typeof body === 'object' && body !== null &&\n    String((body as { error?: string }).error).includes('unreplicated delete');\n}","tryCatchPattern":"const res = await del(url);\nif (res.status === 503 && (await res.json()).error.includes('cloud sync unavailable')) {\n  // deliberate policy decision required: reconfigure sync, or purge sync_entity_heads\n  await notifyUser('delete blocked to protect replicas');\n}","preventionTips":["Keep cloud sync configured on any machine whose data has ever replicated","When decommissioning sync intentionally, plan the sync_entity_heads cleanup as part of the runbook","Never auto-retry this 503 — it encodes a policy refusal, not a transient condition"],"tags":["http-503","cloud-sync","replication","delete","data-integrity"],"backgroundTag":"unreplicated-delete-refused","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}