thedotmack/claude-mem · error · Error
projection_error: invalid projected through_seq
Error message
projection_error: invalid projected through_seq
What it means
advanceProjectionCheckpoint() validates that throughSeq is a legal new checkpoint: it must not regress below the current projected_seq and must not exceed the log's head_seq. A through value outside [projected, head] would either rewind durable progress or checkpoint operations that were never appended, so the DO throws this projectionError.
Solutions
- Always pass the through_seq exactly as returned by getProjectionPage (page.through_seq) rather than a locally computed value.
- Before advancing, compare against getProjectionState(): skip the call if projected_seq >= your through (already done), and re-page if your page is stale.
- Use the through_seq echoed by the current lease/page cycle; discard any through values from before the last epoch change.
- If through > head persists, your page target was invalid — re-clamp target to head_seq and re-acquire the lease.
Example fix
// before await hub.advanceProjectionCheckpoint(token, epoch, from, myLocalAppliedSeq); // after const st = await hub.getProjectionState(); if (st.projected_seq >= page.through_seq) return; // idempotent skip await hub.advanceProjectionCheckpoint(token, epoch, page.from_seq_exclusive, page.through_seq);
Defensive patterns
Strategy: validation
Validate before calling
const st = await hub.getProjectionState();
if (throughSeq < st.projected_seq || throughSeq > st.head_seq) {
throw new RangeError('through_seq outside [projected, head]');
} Type guard
function isLegalThrough(through: string, projected: string, head: string): boolean {
return compareCanonicalDecimals(through, projected) >= 0 &&
compareCanonicalDecimals(through, head) <= 0;
} Try / catch
try {
await hub.advanceProjectionCheckpoint(token, epoch, from, through);
} catch (e) {
if (String(e).includes('invalid projected through_seq')) {
const st = await hub.getProjectionState();
if (st.projected_seq >= through) return; // already advanced; skip
throw e;
}
throw e;
} Prevention
- Pass page.through_seq verbatim from getProjectionPage output
- Skip advancement when projected_seq already >= through (idempotency)
- Discard through values from before the latest epoch change
When it happens
Trigger: Calling advanceProjectionCheckpoint with throughSeq < current projected_seq (replayed/stale page) or throughSeq > headSeq() (caller invented a target ahead of the log); also a through from a previous epoch's numbering.
Common situations: Projector retrying a page after checkpoint already advanced (regression case); caller computing through from its own local applied-op counter that drifted from hub head; epoch rotation making old sequence numbering invalid.
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
- projection_error: target_seq exceeds head_seq
- sync hub push: sent operation is not covered by…
- projection_error: checkpoint compare-and-set mismatch
- projection_error: epoch mismatch
- projection_error: unprojected log gap
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/6483bc120534034e.
Report an issue: GitHub.
Appendix: source
Thrown at workers/sync-hub/src/do/SyncHub.ts:758
advanceProjectionCheckpoint(
leaseToken: string,
epoch: string,
fromSeqExclusive: string,
throughSeq: string,
now = Date.now(),
): ProjectionState {
this.ctx.storage.transactionSync(() => {
// Token check and checkpoint compare-and-set are fenced in the same
// synchronous transaction. A timed-out predecessor can never replay
// after a successor has acquired a fresh token.
this.assertLease(leaseToken, now);
if (epoch !== this.meta("epoch")) throw projectionError("epoch mismatch");
const expected = assertCanonicalDecimal(fromSeqExclusive);
const through = assertCanonicalDecimal(throughSeq);
const current = this.projectedSeq();
if (current !== expected) throw projectionError("checkpoint compare-and-set mismatch");
if (compareCanonicalDecimals(through, current) < 0 || compareCanonicalDecimals(through, this.headSeq()) > 0) {
throw projectionError("invalid projected through_seq");
}
this.setMeta("projected_seq", through);
this.setMeta("projection_lease_expires_at", this.leaseExpiry(this.leaseNow(now)));
});
return this.getProjectionState();
}
releaseProjectionLease(leaseToken: string): void {
if (this.metaOptional("projection_lease_token") !== leaseToken) return;
this.ctx.storage.transactionSync(() => {
this.deleteMeta("projection_lease_token");
this.deleteMeta("projection_lease_expires_at");
});
}
getProjectionState(): ProjectionState {
return {
protocol_version: 1,View on GitHub (pinned to d8bc9755e7)