thedotmack/claude-mem · error

sync hub push: response requires decimal-string…

Error message

sync hub push: response requires decimal-string head_seq/projected_seq

What it means

The push response's head_seq or projected_seq is not a JSON string (a number, null, or missing). Protocol v2 transmits uint64 sequences as decimal strings because JSON numbers lose precision past 2^53; this is a typeof check only, with assertCanonicalDecimal verifying canonical form immediately after. Nothing is stamped locally; flush() retries via backoff.

Solutions

  1. Inspect the raw push response (curl with an empty ops array) and confirm both fields are quoted decimal strings
  2. Fix the hub serializer: String(headSeq), String(projectedSeq) before JSON encoding
  3. Align hub and client versions so both speak protocol v2
  4. Add a contract test on the hub asserting typeof body.head_seq === 'string'

Example fix

// hub push handler — before
return Response.json({ acked, head_seq: head, projected_seq: projected });

// after
return Response.json({ acked, head_seq: String(head), projected_seq: String(projected) });
Defensive patterns

Strategy: type-guard

Validate before calling

// Hub-side self-check before responding:
const payload = { acked, head_seq: String(headSeq), projected_seq: String(projectedSeq) };
if (typeof payload.head_seq !== 'string' || typeof payload.projected_seq !== 'string') throw new Error('bad envelope');

Type guard

function isDecimalStringPair(v: { head_seq?: unknown; projected_seq?: unknown }): v is { head_seq: string; projected_seq: string } {
  return typeof v.head_seq === 'string' && typeof v.projected_seq === 'string';
}

Try / catch

if (/decimal-string head_seq\/projected_seq/.test(sync.status().lastError ?? '')) {
  // typeof violation, not formatting: the hub emitted numbers/null — fix the hub serializer
}

Prevention

When it happens

Trigger: Hub serializes the sequences as numbers; one field renamed or dropped in a hub build; an intermediate transform re-encodes them; stub responses in tests using integers.

Common situations: Hub/client version skew after upgrading only one side; a hub port from another language defaulting to integer JSON; contract drift introduced during hub refactoring.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/services/sync/CloudSync.ts:984

    }
    if (!res.ok) {
      const body = (await res.text().catch(() => '')).slice(0, 200);
      throw new Error(`sync hub push ${res.status}: ${body}`);
    }
    let parsed: unknown;
    try {
      parsed = await res.json();
    } catch {
      throw new Error('sync hub push: response is not JSON');
    }
    const acked = (parsed as { acked?: unknown } | null)?.acked;
    if (!Array.isArray(acked)) {
      throw new Error('sync hub push: response missing acked array');
    }
    const headSeq = (parsed as { head_seq?: unknown }).head_seq;
    const projectedSeq = (parsed as { projected_seq?: unknown }).projected_seq;
    if (typeof headSeq !== 'string' || typeof projectedSeq !== 'string') {
      throw new Error('sync hub push: response requires decimal-string head_seq/projected_seq');
    }
    assertCanonicalDecimal(headSeq);
    assertCanonicalDecimal(projectedSeq);
    const validatedAcked = acked.map((value, index): AckedOp => {
      if (!value || typeof value !== 'object' || Array.isArray(value)) {
        throw new Error(`sync hub push: acked[${index}] must be an object`);
      }
      const item = value as Record<string, unknown>;
      if (
        typeof item.id !== 'string'
        || typeof item.kind !== 'string'
        || (item.origin_local_id !== null && typeof item.origin_local_id !== 'string')
        || typeof item.entity_rev !== 'string'
        || typeof item.operation_sha256 !== 'string'
        || typeof item.seq !== 'string'
      ) {
        throw new Error(`sync hub push: malformed acked[${index}]`);
      }

View on GitHub (pinned to e2d1df569a)