thedotmack/claude-mem · error · Error
sync hub status: projected_seq exceeds head_seq
Error message
sync hub status: projected_seq exceeds head_seq
What it means
Within a given epoch, projected_seq must never exceed head_seq (projection cannot run ahead of the head). probeHubStatus compares the two canonical decimals and throws 'sync hub status: projected_seq exceeds head_seq' when the hub reports an inconsistent state, refusing to build sync decisions on it.
Solutions
- Treat the hub as inconsistent: re-probe after a short delay to see if the hub self-corrects.
- Check hub logs for head-sequence resets or failover events and repair hub state (recompute projected_seq or rebuild from the log).
- Verify hub write ordering guarantees: head_seq must be advanced atomically before/with projected_seq updates.
- If reproducible, report/fix the hub race where projection can be observed ahead of head.
Example fix
// before (hub, unsynchronized writes)
await store.set('projected_seq', nextProjected);
await store.set('head_seq', nextHead);
// after: advance head first, then projection
await store.set('head_seq', nextHead);
await store.set('projected_seq', Math.min(nextProjected, nextHead)); Defensive patterns
Strategy: validation
Validate before calling
function seqOrderOk(status: { head_seq: string; projected_seq: string }): boolean {
return compareCanonicalDecimals(status.projected_seq, status.head_seq) <= 0;
}
if (!seqOrderOk(status)) throw new Error('hub reported projected_seq > head_seq; hub state inconsistent'); Try / catch
try {
const status = await cloudSync.statusWithHubProbe();
} catch (e) {
if (e.message === 'sync hub status: projected_seq exceeds head_seq') {
logger.error('hub invariant violated; re-probing after backoff', { hubUrl });
await sleep(5000);
status = await cloudSync.statusWithHubProbe();
} else throw e;
} Prevention
- Advance head_seq atomically before publishing projected_seq on the hub
- Verify hub state after crash recovery/failover (recompute projected_seq)
- Re-probe once before treating this as a permanent hub fault — races can self-heal
- Add hub-side invariant assertions: projected_seq must never exceed head_seq
When it happens
Trigger: Hub returns projected_seq > head_seq, typically from a race between head advancement and projection publication, or from a buggy/reset head counter on the hub.
Common situations: Hub crash/recovery resetting head_seq while projections persisted, concurrent writers racing during compaction, clock/counter corruption after failover, hand-crafted status fixtures.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- agent_event source_id must equal agent_event_id
- canonical content
- projection_error: invalid projected through_seq
- projection_error: target_seq exceeds head_seq
- projection_error: unprojected log gap
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/1aec00fc157a2e19.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:683
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('sync hub status: response must be an object');
}
const record = parsed as Record<string, unknown>;
if (record.protocol_version !== 2) {
throw new Error('sync hub status: response requires protocol_version 2');
}
if (
typeof record.epoch !== 'string'
|| typeof record.head_seq !== 'string'
|| typeof record.projected_seq !== 'string'
) {
throw new Error('sync hub status: response requires decimal-string epoch/head_seq/projected_seq');
}
const epoch = assertCanonicalDecimal(record.epoch, { positive: true });
const headSeq = assertCanonicalDecimal(record.head_seq);
const projectedSeq = assertCanonicalDecimal(record.projected_seq);
if (compareCanonicalDecimals(projectedSeq, headSeq) > 0) {
throw new Error('sync hub status: projected_seq exceeds head_seq');
}
this.hubStatus = {
checkedAt,
reachable: true,
epoch,
headSeq,
projectedSeq,
error: null,
};
} catch (error) {
const raw = error instanceof Error ? error.message : String(error);
const safe = this.token === '' ? raw : raw.split(this.token).join('[REDACTED]');
this.hubStatus = {
checkedAt,
reachable: false,
epoch: null,
headSeq: null,
projectedSeq: null,View on GitHub (pinned to d8bc9755e7)