{"record":{"id":"6483bc120534034e","repo":"thedotmack/claude-mem","slug":"projection-error-invalid-projected-through-seq","errorCode":null,"errorMessage":"projection_error: invalid projected through_seq","messagePattern":"projection_error: invalid projected through_seq","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/do/SyncHub.ts","lineNumber":758,"sourceCode":"\tadvanceProjectionCheckpoint(\n\t\tleaseToken: string,\n\t\tepoch: string,\n\t\tfromSeqExclusive: string,\n\t\tthroughSeq: string,\n\t\tnow = Date.now(),\n\t): ProjectionState {\n\t\tthis.ctx.storage.transactionSync(() => {\n\t\t\t// Token check and checkpoint compare-and-set are fenced in the same\n\t\t\t// synchronous transaction. A timed-out predecessor can never replay\n\t\t\t// after a successor has acquired a fresh token.\n\t\t\tthis.assertLease(leaseToken, now);\n\t\t\tif (epoch !== this.meta(\"epoch\")) throw projectionError(\"epoch mismatch\");\n\t\t\tconst expected = assertCanonicalDecimal(fromSeqExclusive);\n\t\t\tconst through = assertCanonicalDecimal(throughSeq);\n\t\t\tconst current = this.projectedSeq();\n\t\t\tif (current !== expected) throw projectionError(\"checkpoint compare-and-set mismatch\");\n\t\t\tif (compareCanonicalDecimals(through, current) < 0 || compareCanonicalDecimals(through, this.headSeq()) > 0) {\n\t\t\t\tthrow projectionError(\"invalid projected through_seq\");\n\t\t\t}\n\t\t\tthis.setMeta(\"projected_seq\", through);\n\t\t\tthis.setMeta(\"projection_lease_expires_at\", this.leaseExpiry(this.leaseNow(now)));\n\t\t});\n\t\treturn this.getProjectionState();\n\t}\n\n\treleaseProjectionLease(leaseToken: string): void {\n\t\tif (this.metaOptional(\"projection_lease_token\") !== leaseToken) return;\n\t\tthis.ctx.storage.transactionSync(() => {\n\t\t\tthis.deleteMeta(\"projection_lease_token\");\n\t\t\tthis.deleteMeta(\"projection_lease_expires_at\");\n\t\t});\n\t}\n\n\tgetProjectionState(): ProjectionState {\n\t\treturn {\n\t\t\tprotocol_version: 1,","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/do/SyncHub.ts#L740-L776","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait hub.advanceProjectionCheckpoint(token, epoch, from, myLocalAppliedSeq);\n// after\nconst st = await hub.getProjectionState();\nif (st.projected_seq >= page.through_seq) return; // idempotent skip\nawait hub.advanceProjectionCheckpoint(token, epoch, page.from_seq_exclusive, page.through_seq);","handlingStrategy":"validation","validationCode":"const st = await hub.getProjectionState();\nif (throughSeq < st.projected_seq || throughSeq > st.head_seq) {\n  throw new RangeError('through_seq outside [projected, head]');\n}","typeGuard":"function isLegalThrough(through: string, projected: string, head: string): boolean {\n  return compareCanonicalDecimals(through, projected) >= 0 &&\n         compareCanonicalDecimals(through, head) <= 0;\n}","tryCatchPattern":"try {\n  await hub.advanceProjectionCheckpoint(token, epoch, from, through);\n} catch (e) {\n  if (String(e).includes('invalid projected through_seq')) {\n    const st = await hub.getProjectionState();\n    if (st.projected_seq >= through) return; // already advanced; skip\n    throw e;\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["projection","consistency","checkpoint","sync"],"backgroundTag":"value-out-of-range","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}