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

  1. Treat the hub as inconsistent: re-probe after a short delay to see if the hub self-corrects.
  2. Check hub logs for head-sequence resets or failover events and repair hub state (recompute projected_seq or rebuild from the log).
  3. Verify hub write ordering guarantees: head_seq must be advanced atomically before/with projected_seq updates.
  4. 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

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


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)