thedotmack/claude-mem · error
sync hub push: acknowledgment seq exceeds head_seq
Error message
sync hub push: acknowledgment seq exceeds head_seq
What it means
An acked operation's seq is greater than the head_seq reported in the same response — the hub acknowledged a log position beyond the durable head it itself declares. Internal inconsistency; caught before stampAcked, so nothing changes locally and flush() retries with backoff.
Solutions
- Reproduce with a single-op push and compare ack.seq against reported head_seq
- On the hub, derive head_seq and ack seqs from one consistent point (same transaction/read) when assembling the response
- Ensure reads for the response come from the authoritative log, not a lagging replica
- Redeploy the fixed hub; clients recover automatically on the next retry
Defensive patterns
Strategy: validation
Validate before calling
// Hub-side self-check: no ack may outrun the reported head:
const maxAckSeq = acked.reduce((m, a) => (BigInt(a.seq) > BigInt(m) ? a.seq : m), '0');
if (BigInt(maxAckSeq) > BigInt(headSeq)) throw new Error('ack seq beyond head — refuse to respond'); Type guard
function acksWithinHead(acked: { seq: string }[], head_seq: string): boolean {
return acked.every(a => BigInt(a.seq) <= BigInt(head_seq));
} Try / catch
if (/acknowledgment seq exceeds head_seq/.test(sync.status().lastError ?? '')) {
// inconsistent response assembly on the hub: compute head and acks from one transaction/read
} Prevention
- Assemble the push response (acks, head_seq, projected_seq) from a single consistent snapshot
- Never read sequence fields from lagging replicas for the response path
- Add a hub invariant test covering concurrent appends during response assembly
When it happens
Trigger: head_seq computed from a stale replica while acks carry fresh seqs; a hub bug computing head_seq before the append transaction commits; log truncation/rollback between computing head and emitting acks.
Common situations: Hub scaled to multi-replica reads without read-your-writes; a hub refactor reordered head computation vs append; snapshot isolation issues in the hub's response assembly.
Related errors
- sync hub push: 200 response acknowledgment multiplicity…
- sync hub push: 200 response contains an extra or mismatched…
- sync hub push: checkpoint order requires head_seq <=…
- sync hub push: distinct operation tuples claimed the same…
- sync hub push: duplicate operation tuple claimed different…
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/60ec1ff581ef1cda.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:1074
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');
}
}
}
/**
* Stamp rows / delete outbox entries for a fully validated acknowledgment
* multiset. The hub may return entries in any order.
*/
private stampAcked(acked: AckedOp[], pushed: WireOp[]): void {
const now = Date.now();
const bodies = new Map(pushed.map(op => {
const body = parseCanonicalOperation(op);
return [operationTupleKey({
id: body.id,
kind: body.kind,View on GitHub (pinned to e2d1df569a)