thedotmack/claude-mem · error
sync hub push: 200 response acknowledgment multiplicity…
Error message
sync hub push: 200 response acknowledgment multiplicity mismatch (expected ${expected}, received ${actual}) What it means
Per-tuple acknowledgment counts don't match what was sent: a tuple pushed N times was acked M≠N times. Duplicates in one push are legitimate (the same entity/rev op can appear in more than one lane), which is why counts, not just presence, are checked. Caught before stamping; flush() retries with backoff. Usually means the hub collapsed duplicate submissions into one ack while the client sent copies, or acked one copy too many.
Solutions
- Log sentCounts vs ackCounts around the failing push to see which tuple's multiplicity differs
- Align hub behavior with the contract: every pushed copy gets one ack entry, including duplicates
- Or, if the hub intentionally coalesces, dedupe identical tuples client-side before packing the batch
- Check whether a re-enqueue path can queue the exact same (entity_id, entity_rev) twice in one drain and prevent it
Example fix
// before: same tuple can enter the batch twice
buf.push(op); buf.push(op);
// after: dedupe by tuple key when packing
const key = operationTupleKey({ id, kind, entity_rev, operation_sha256 });
if (!seen.has(key)) { seen.add(key); buf.push(op); } Defensive patterns
Strategy: try-catch
Validate before calling
// If you assemble batches yourself, dedupe identical tuples before packing:
const seen = new Set<string>();
const packed = ops.filter(op => {
const body = parseCanonicalOperation(op);
const key = JSON.stringify([body.id, body.kind, body.entity_rev, op.operation_sha256]);
if (seen.has(key)) return false;
seen.add(key);
return true;
}); Try / catch
const m = (sync.status().lastError ?? '').match(/multiplicity mismatch \(expected (\d+), received (\d+)\)/);
if (m) {
// expected>received: hub coalesced duplicate copies — align on per-copy acks or dedupe client-side
} Prevention
- Agree on duplicate-op ack semantics (per sent copy) between client and hub, in the contract tests
- Avoid enqueue paths that can queue the identical (entity_id, entity_rev) tuple twice in one drain
- Cover duplicate-tuple pushes in hub fixtures
When it happens
Trigger: The same op tuple queued twice locally (e.g. content lane plus a frozen re-enqueue) and the hub's dedupe acks it once; hub coalesces duplicate appends into one ack entry but the client counts two; a hub contract change on duplicate handling.
Common situations: Version skew where the hub changed from per-copy acks to per-unique-op acks; requeued retries producing duplicate tuples in a single batch; migration paths that legitimately double-enqueue.
Related errors
- sync hub push: 200 response contains an extra or mismatched…
- sync hub push: acknowledgment seq exceeds head_seq
- sync hub push: duplicate operation tuple claimed different…
- sync hub push: checkpoint order requires head_seq <=…
- sync hub push: distinct operation tuples claimed the same…
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/f93e0925e96b98df.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:1059
ackCounts.set(key, (ackCounts.get(key) ?? 0) + 1);
const priorTupleSeq = tupleSeq.get(key);
if (priorTupleSeq !== undefined && priorTupleSeq !== ack.seq) {
throw new Error('sync hub push: duplicate operation tuple claimed different sequences');
}
tupleSeq.set(key, ack.seq);
const priorSeqTuple = seqTuple.get(ack.seq);
if (priorSeqTuple !== undefined && priorSeqTuple !== key) {
throw new Error('sync hub push: distinct operation tuples claimed the same sequence');
}
seqTuple.set(ack.seq, key);
}
for (const [key, expected] of sentCounts) {
const actual = ackCounts.get(key) ?? 0;
if (actual !== expected) {
throw new Error(
`sync hub push: 200 response acknowledgment multiplicity mismatch (expected ${expected}, received ${actual})`
);
}
}
if (ackCounts.size !== sentCounts.size) {
// Defensive: the unknown-tuple branch above should make this impossible.
throw new Error('sync hub push: 200 response acknowledgment multiset mismatch');
}
if (compareCanonicalDecimals(response.head_seq, response.projected_seq) > 0) {
throw new Error('sync hub push: checkpoint order requires head_seq <= projected_seq');
}
for (const ack of response.acked) {
if (compareCanonicalDecimals(ack.seq, response.head_seq) > 0) {
throw new Error('sync hub push: acknowledgment seq exceeds head_seq');
}
if (compareCanonicalDecimals(ack.seq, response.projected_seq) > 0) {
throw new Error('sync hub push: sent operation is not covered by projected_seq');View on GitHub (pinned to e2d1df569a)