thedotmack/claude-mem · error

SyncApply: sequence gap (expected ${incrementCanonicalDecima

Error message

SyncApply: sequence gap (expected ${incrementCanonicalDecimal(lastSeq)}, got ${seq})

What it means

Thrown by SyncApply when applying a page of ops in strict-contiguous mode (options.requireContiguous === true) and an op's seq is not exactly lastSeq + 1. Strict HTTP pages must describe the precise suffix after the cursor with no gaps; a gap means the page is incomplete or reordered, which would silently drop ops if not enforced.

Source

Thrown at src/services/sync/SyncApply.ts:462

      epochReset: false,
    };
    if (ops.length === 0) return result;

    const chromaJobs: ChromaJob[] = [];

    const tx = this.db.transaction(() => {
      const cursor = this.getCursor();
      let lastSeq = cursor;

      for (const op of ops) {
        const seq = assertCanonicalDecimal(op.seq, { positive: true });
        assertCanonicalDecimal(op.rev, { positive: true });
        // Strict HTTP pages describe the exact raw suffix after our cursor.
        // Validate every supplied sequence before the ordinary replay skip;
        // otherwise a stale prefix (even an out-of-order one) is silently
        // discarded and a malformed page can look contiguous.
        if (options.requireContiguous === true && seq !== incrementCanonicalDecimal(lastSeq)) {
          throw new Error(`SyncApply: sequence gap (expected ${incrementCanonicalDecimal(lastSeq)}, got ${seq})`);
        }
        if (compareCanonicalDecimals(seq, cursor) <= 0) {
          result.skippedCursor++;
          continue;
        }
        if (compareCanonicalDecimals(seq, lastSeq) <= 0) {
          throw new Error(`SyncApply: ops out of order (seq ${op.seq} after ${lastSeq})`);
        }
        lastSeq = seq;

        if (op.origin_device === this.deviceId) {
          result.skippedOwn++;
          continue;
        }

        let outcome: 'applied' | 'stale';
        if (op.kind === 'mutation') {
          outcome = this.applyMutation(op);

View on GitHub (pinned to d768ba3643)

Solutions

  1. If the page genuinely has a gap, re-fetch from the current cursor (the hub must return a contiguous suffix).
  2. If you do not need strict contiguity, call applyOps without requireContiguous (or set it false) to allow cursor-based skipping.
  3. Verify the cursor used to request the page matches the local getCursor() at apply time — a moved cursor produces apparent gaps.
  4. Check hub pagination for an off-by-one that drops the first/last op of a page.

Example fix

// before: apply.applyOps(page, { requireContiguous: true }) // throws on any gap
// after:  apply.applyOps(page, { requireContiguous: false }) // skip already-applied via cursor
Defensive patterns

Strategy: validation

Validate before calling

// Before applyOps, decide whether strict contiguity is required by the page source:
function shouldRequireContiguous(pageSource: 'strict-http' | 'backfill'): boolean {
  return pageSource === 'strict-http';
}
// If gaps are possible, pass requireContiguous: false and rely on cursor skipping.
const opts = { requireContiguous: shouldRequireContiguous(source) };

Try / catch

try { apply.applyOps(page, { requireContiguous: true }); }
catch (e) {
  if (e instanceof Error && e.message.startsWith('SyncApply: sequence gap')) {
    // re-fetch from current cursor; strict page was incomplete
    page = await refetchFromCursor(apply.getCursor());
    apply.applyOps(page, { requireContiguous: true });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: In the applyOps transaction, for each op: assertCanonicalDecimal(op.seq), then if requireContiguous and seq !== incrementCanonicalDecimal(lastSeq), throw. lastSeq starts at the cursor. Even a stale-prefix op is checked before the cursor-skip, so an out-of-order stale prefix also trips it.

Common situations: Caller passed requireContiguous:true for a strict HTTP page that had a missing seq (hub omitted an op), a page fetched from a cursor that moved between request and response, or non-contiguous data fed into a strict-merge path. Indicates the page boundary contract was violated.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/20a4120e8b007ac6. Report an issue: GitHub.