{"record":{"id":"9b68d43f0987777c","repo":"thedotmack/claude-mem","slug":"syncapply-ops-out-of-order-seq-op-seq-after","errorCode":null,"errorMessage":"SyncApply: ops out of order (seq ${op.seq} after ${lastSeq})","messagePattern":"SyncApply: ops out of order \\(seq (.+?) after (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sync/SyncApply.ts","lineNumber":469,"sourceCode":"      const cursor = this.getCursor();\n      let lastSeq = cursor;\n\n      for (const op of ops) {\n        const seq = assertCanonicalDecimal(op.seq, { positive: true });\n        assertCanonicalDecimal(op.rev, { positive: true });\n        // Strict HTTP pages describe the exact raw suffix after our cursor.\n        // Validate every supplied sequence before the ordinary replay skip;\n        // otherwise a stale prefix (even an out-of-order one) is silently\n        // discarded and a malformed page can look contiguous.\n        if (options.requireContiguous === true && seq !== incrementCanonicalDecimal(lastSeq)) {\n          throw new Error(`SyncApply: sequence gap (expected ${incrementCanonicalDecimal(lastSeq)}, got ${seq})`);\n        }\n        if (compareCanonicalDecimals(seq, cursor) <= 0) {\n          result.skippedCursor++;\n          continue;\n        }\n        if (compareCanonicalDecimals(seq, lastSeq) <= 0) {\n          throw new Error(`SyncApply: ops out of order (seq ${op.seq} after ${lastSeq})`);\n        }\n        lastSeq = seq;\n\n        if (op.origin_device === this.deviceId) {\n          result.skippedOwn++;\n          continue;\n        }\n\n        let outcome: 'applied' | 'stale';\n        if (op.kind === 'mutation') {\n          outcome = this.applyMutation(op);\n        } else {\n          outcome = this.applyCanonicalRowOp(op, chromaJobs);\n        }\n        if (outcome === 'applied') result.applied++;\n        else result.skippedStale++;\n      }\n","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/sync/SyncApply.ts#L451-L487","documentation":"Thrown by SyncApply when an op's seq is <= lastSeq but > cursor — i.e. it is past the cursor (so not a replay-skip) yet not greater than the highest seq already seen in this same batch. Ops within a page must be strictly monotonically increasing; a duplicate or regression triggers this.","triggerScenarios":"In applyOps, after the contiguous check: if compareCanonicalDecimals(seq, cursor) <= 0 it is skipped as already-applied; otherwise if compareCanonicalDecimals(seq, lastSeq) <= 0 it throws. Fires when a page contains a seq that goes backward relative to an earlier op in the same page (and is not behind the cursor).","commonSituations":"Hub returned a page with non-monotonic seq ordering, a duplicate op within one page, or a page stitched from two overlapping fetches. The contiguous check [131] only runs when requireContiguous is true, but this out-of-order check always runs, so it catches unordered pages even in non-strict mode.","solutions":["Inspect the page for the seq that regressed relative to lastSeq (the message names both seq and lastSeq).","Ensure the hub returns ops sorted by ascending seq within a page.","Re-request the page from the current cursor; do not merge overlapping pages client-side.","If duplicates are expected from the hub, dedup by seq before calling applyOps."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-sort/dedup a page by seq before applying (only if hub ordering is unreliable):\nfunction normalizePage(ops: SyncOp[]): SyncOp[] {\n  return ops\n    .filter((op, i, arr) => arr.findIndex(o => o.seq === op.seq) === i) // dedup by seq\n    .sort((a, b) => compareCanonicalDecimals(a.seq, b.seq));\n}","typeGuard":null,"tryCatchPattern":"try { apply.applyOps(page, opts); }\ncatch (e) {\n  if (e instanceof Error && e.message.startsWith('SyncApply: ops out of order')) {\n    // hub returned non-monotonic page; sort+dedup then retry without strict contiguity\n    apply.applyOps(normalizePage(page), { requireContiguous: false }); return;\n  }\n  throw e;\n}","preventionTips":["Expect the hub to return ops sorted by ascending seq; report if it does not.","Do not stitch overlapping pages client-side — fetch each from the cursor.","If duplicate seqs are expected, dedup before applyOps."],"tags":["sync","sync-apply","validation","sequence-integrity"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}