{"record":{"id":"78a23a61f5bbd06d","repo":"thedotmack/claude-mem","slug":"revision-hash-conflict-body-id-body-entity-rev","errorCode":null,"errorMessage":"revision_hash_conflict:${body.id}:${body.entity_rev}","messagePattern":"revision_hash_conflict:(.+?):(.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/do/SyncHub.ts","lineNumber":417,"sourceCode":"\t\tconst nowDecimal = String(now);\n\t\tconst headBefore = this.headSeq();\n\t\tconst acked: AckedOp[] = [];\n\t\ttry {\n\t\t\tthis.ctx.storage.transactionSync(() => {\n\t\t\t\tthis.touchDevice(deviceId, normalizeDeviceName(deviceName), now);\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tconst body = row.body;\n\t\t\t\t\tconst head = sql.exec<HeadRow>(\n\t\t\t\t\t\t`SELECT entity_rev, operation_sha256, deleted, CAST(seq AS TEXT) AS seq\n\t\t\t\t\t\t FROM entity_heads WHERE entity_id = ?`,\n\t\t\t\t\t\tbody.id,\n\t\t\t\t\t).toArray()[0];\n\t\t\t\t\tif (head) {\n\t\t\t\t\t\tconst order = compareCanonicalDecimals(body.entity_rev, head.entity_rev);\n\t\t\t\t\t\tif (order < 0) throw invalid(`stale_revision:${body.id}:${body.entity_rev}<${head.entity_rev}`);\n\t\t\t\t\t\tif (order === 0) {\n\t\t\t\t\t\t\tif (row.operationSha256 !== head.operation_sha256) {\n\t\t\t\t\t\t\t\tthrow invalid(`revision_hash_conflict:${body.id}:${body.entity_rev}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tacked.push({\n\t\t\t\t\t\t\t\tid: body.id,\n\t\t\t\t\t\t\t\tkind: body.kind,\n\t\t\t\t\t\t\t\torigin_local_id: body.origin_local_id,\n\t\t\t\t\t\t\t\tentity_rev: body.entity_rev,\n\t\t\t\t\t\t\t\toperation_sha256: head.operation_sha256,\n\t\t\t\t\t\t\t\tseq: String(head.seq),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst seq = incrementCanonicalDecimal(this.headSeq());\n\t\t\t\t\tsql.exec(\n\t\t\t\t\t\t`INSERT INTO canonical_ops\n\t\t\t\t\t\t (seq, entity_id, kind, origin_device_id, origin_local_id, entity_rev,\n\t\t\t\t\t\t  operation_sha256, body, deleted, server_ts)","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/do/SyncHub.ts#L399-L435","documentation":"Idempotency/hash guard in SyncHubDO.pushOps: the op's entity_rev equals the current head revision but its operation_sha256 differs from the hash stored at that head — two different operations claim the same revision, which is a hash conflict, not a replay. The op is refused to protect the integrity of the canonical op log.","triggerScenarios":"Two devices independently generate the same entity_rev (e.g. both compute rev 5) with different content; non-deterministic content hashing client-side; a rebased op colliding with an already-acked revision.","commonSituations":"Divergent offline edits that both pick the same next revision, custom revision schemes that reuse counters after a restore, hash algorithm change between client versions.","solutions":["Regenerate the op with a fresh, strictly greater entity_rev and re-push","Ensure the client hashes canonical operation bytes with the exact same algorithm as the hub","Never reuse a revision number: always derive from the latest observed head plus one increment"],"exampleFix":"// before\nconst rev = nextRev(localCounter); // may collide\n// after\nconst rev = bumpRevision(await fetchEntityHead(entity.id).entity_rev);","handlingStrategy":"retry","validationCode":"const head = await fetchEntityHead(entityId);\nif (op.entity_rev === head.entity_rev && hashCanonical(op) !== head.operation_sha256) {\n  op = { ...op, entity_rev: bumpRevision(head.entity_rev) };\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await hub.push(deviceId, ops);\n} catch (e) {\n  if (String(e).startsWith('revision_hash_conflict:')) return rePushWithBumpedRevision(e, ops);\n  throw e;\n}","preventionTips":["Never reuse revision numbers: derive from the latest head","Use the exact canonical hashing algorithm the hub expects","For concurrent editors, prefer random/Lamport-style revision tiebreakers"],"tags":["sync","conflict","hash","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}