thedotmack/claude-mem · error

sync hub push: malformed acked

Error message

sync hub push: malformed acked[${index}]

What it means

An acked[] entry has a missing or wrong-typed required field (id, kind, origin_local_id, entity_rev, operation_sha256, seq), or one of its decimal strings is non-canonical — assertCanonicalDecimal rejects leading zeros, signs, values above uint64, and 0 for entity_rev/seq (positive required). Validation precedes stamping; flush() backs off and retries unchanged.

Solutions

  1. Dump the offending ack entry (wrap sendOps in a logger or curl a minimal push) and compare each field against the contract
  2. Fix field emission: origin_local_id must be null-or-decimal-string, entity_rev/seq positive canonical decimals without padding
  3. Reject sequences above uint64 at the hub's append path (that is also a client-side uint64 overflow signal)
  4. Add a hub-side schema test that validates its own responses with the same canonical-decimal rules

Example fix

// hub — before: zero-padded log formatting leaks to the wire
acked.push({ id, kind, origin_local_id, entity_rev, operation_sha256, seq: seq.toString().padStart(6, '0') });

// after: canonical decimal, no padding
acked.push({ id, kind, origin_local_id, entity_rev, operation_sha256, seq: String(seq) });
Defensive patterns

Strategy: type-guard

Validate before calling

// Full ack-entry validation mirror (hub contract tests):
const CANON = /^(?:0|[1-9][0-9]{0,19})$/;
function isValidAckedOp(a: unknown): boolean {
  if (!a || typeof a !== 'object' || Array.isArray(a)) return false;
  const i = a as Record<string, unknown>;
  return typeof i.id === 'string' && typeof i.kind === 'string'
    && (i.origin_local_id === null || (typeof i.origin_local_id === 'string' && CANON.test(i.origin_local_id)))
    && typeof i.entity_rev === 'string' && CANON.test(i.entity_rev) && i.entity_rev !== '0'
    && typeof i.operation_sha256 === 'string'
    && typeof i.seq === 'string' && CANON.test(i.seq) && i.seq !== '0';
}

Type guard

function isCanonicalDecimal(v: unknown, positive = false): v is string {
  return typeof v === 'string' && /^(?:0|[1-9][0-9]{0,19})$/.test(v) && (!positive || v !== '0');
}

Try / catch

const m = (sync.status().lastError ?? '').match(/malformed acked\[(\d+)\]/);
if (m) {
  // index tells you which entry to inspect in the captured hub response; check field types and decimal form
}

Prevention

When it happens

Trigger: Hub omits origin_local_id for mutation acks (must be null, not undefined/missing after serialization quirks); seq serialized as number then coerced; entity_rev='0' emitted for a first revision; hub emits zero-padded sequences like '0042'; a 20+ digit seq exceeding uint64.

Common situations: Hub ported to another language with different number-formatting defaults; format-string padding introduced for logs leaking into the wire body; version skew; hand-maintained hub fixtures.

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/44f9374f94e4e9ab. Report an issue: GitHub.

Appendix: source

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

    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}]`);
      }
      assertCanonicalDecimal(item.entity_rev, { positive: true });
      assertCanonicalDecimal(item.seq, { positive: true });
      if (typeof item.origin_local_id === 'string') assertCanonicalDecimal(item.origin_local_id);
      return item as unknown as AckedOp;
    });
    return {
      acked: validatedAcked,
      head_seq: headSeq,
      projected_seq: projectedSeq,
    };
  }

  /**
   * Treat a successful push response as one atomic acknowledgment proof.
   * Nothing in this method mutates SQLite. stampAcked() runs only after every
   * tuple, multiplicity, sequence, and checkpoint invariant has passed.
   */

View on GitHub (pinned to e2d1df569a)