thedotmack/claude-mem · error · Error

revision_hash_conflict

Error message

revision_hash_conflict:${body.id}:${body.entity_rev}

What it means

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.

Solutions

  1. Regenerate the op with a fresh, strictly greater entity_rev and re-push
  2. Ensure the client hashes canonical operation bytes with the exact same algorithm as the hub
  3. Never reuse a revision number: always derive from the latest observed head plus one increment

Example fix

// before
const rev = nextRev(localCounter); // may collide
// after
const rev = bumpRevision(await fetchEntityHead(entity.id).entity_rev);
Defensive patterns

Strategy: retry

Validate before calling

const head = await fetchEntityHead(entityId);
if (op.entity_rev === head.entity_rev && hashCanonical(op) !== head.operation_sha256) {
  op = { ...op, entity_rev: bumpRevision(head.entity_rev) };
}

Try / catch

try {
  return await hub.push(deviceId, ops);
} catch (e) {
  if (String(e).startsWith('revision_hash_conflict:')) return rePushWithBumpedRevision(e, ops);
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at workers/sync-hub/src/do/SyncHub.ts:417

		const nowDecimal = String(now);
		const headBefore = this.headSeq();
		const acked: AckedOp[] = [];
		try {
			this.ctx.storage.transactionSync(() => {
				this.touchDevice(deviceId, normalizeDeviceName(deviceName), now);
				for (const row of rows) {
					const body = row.body;
					const head = sql.exec<HeadRow>(
						`SELECT entity_rev, operation_sha256, deleted, CAST(seq AS TEXT) AS seq
						 FROM entity_heads WHERE entity_id = ?`,
						body.id,
					).toArray()[0];
					if (head) {
						const order = compareCanonicalDecimals(body.entity_rev, head.entity_rev);
						if (order < 0) throw invalid(`stale_revision:${body.id}:${body.entity_rev}<${head.entity_rev}`);
						if (order === 0) {
							if (row.operationSha256 !== head.operation_sha256) {
								throw invalid(`revision_hash_conflict:${body.id}:${body.entity_rev}`);
							}
							acked.push({
								id: body.id,
								kind: body.kind,
								origin_local_id: body.origin_local_id,
								entity_rev: body.entity_rev,
								operation_sha256: head.operation_sha256,
								seq: String(head.seq),
							});
							continue;
						}
					}

					const seq = incrementCanonicalDecimal(this.headSeq());
					sql.exec(
						`INSERT INTO canonical_ops
						 (seq, entity_id, kind, origin_device_id, origin_local_id, entity_rev,
						  operation_sha256, body, deleted, server_ts)

View on GitHub (pinned to d8bc9755e7)