{"record":{"id":"44f9374f94e4e9ab","repo":"thedotmack/claude-mem","slug":"sync-hub-push-malformed-acked-index","errorCode":null,"errorMessage":"sync hub push: malformed acked[${index}]","messagePattern":"sync hub push: malformed acked\\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sync/CloudSync.ts","lineNumber":1001,"sourceCode":"    if (typeof headSeq !== 'string' || typeof projectedSeq !== 'string') {\n      throw new Error('sync hub push: response requires decimal-string head_seq/projected_seq');\n    }\n    assertCanonicalDecimal(headSeq);\n    assertCanonicalDecimal(projectedSeq);\n    const validatedAcked = acked.map((value, index): AckedOp => {\n      if (!value || typeof value !== 'object' || Array.isArray(value)) {\n        throw new Error(`sync hub push: acked[${index}] must be an object`);\n      }\n      const item = value as Record<string, unknown>;\n      if (\n        typeof item.id !== 'string'\n        || typeof item.kind !== 'string'\n        || (item.origin_local_id !== null && typeof item.origin_local_id !== 'string')\n        || typeof item.entity_rev !== 'string'\n        || typeof item.operation_sha256 !== 'string'\n        || typeof item.seq !== 'string'\n      ) {\n        throw new Error(`sync hub push: malformed acked[${index}]`);\n      }\n      assertCanonicalDecimal(item.entity_rev, { positive: true });\n      assertCanonicalDecimal(item.seq, { positive: true });\n      if (typeof item.origin_local_id === 'string') assertCanonicalDecimal(item.origin_local_id);\n      return item as unknown as AckedOp;\n    });\n    return {\n      acked: validatedAcked,\n      head_seq: headSeq,\n      projected_seq: projectedSeq,\n    };\n  }\n\n  /**\n   * Treat a successful push response as one atomic acknowledgment proof.\n   * Nothing in this method mutates SQLite. stampAcked() runs only after every\n   * tuple, multiplicity, sequence, and checkpoint invariant has passed.\n   */","sourceCodeStart":983,"sourceCodeEnd":1019,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/CloudSync.ts#L983-L1019","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dump the offending ack entry (wrap sendOps in a logger or curl a minimal push) and compare each field against the contract","Fix field emission: origin_local_id must be null-or-decimal-string, entity_rev/seq positive canonical decimals without padding","Reject sequences above uint64 at the hub's append path (that is also a client-side uint64 overflow signal)","Add a hub-side schema test that validates its own responses with the same canonical-decimal rules"],"exampleFix":"// hub — before: zero-padded log formatting leaks to the wire\nacked.push({ id, kind, origin_local_id, entity_rev, operation_sha256, seq: seq.toString().padStart(6, '0') });\n\n// after: canonical decimal, no padding\nacked.push({ id, kind, origin_local_id, entity_rev, operation_sha256, seq: String(seq) });","handlingStrategy":"type-guard","validationCode":"// Full ack-entry validation mirror (hub contract tests):\nconst CANON = /^(?:0|[1-9][0-9]{0,19})$/;\nfunction isValidAckedOp(a: unknown): boolean {\n  if (!a || typeof a !== 'object' || Array.isArray(a)) return false;\n  const i = a as Record<string, unknown>;\n  return typeof i.id === 'string' && typeof i.kind === 'string'\n    && (i.origin_local_id === null || (typeof i.origin_local_id === 'string' && CANON.test(i.origin_local_id)))\n    && typeof i.entity_rev === 'string' && CANON.test(i.entity_rev) && i.entity_rev !== '0'\n    && typeof i.operation_sha256 === 'string'\n    && typeof i.seq === 'string' && CANON.test(i.seq) && i.seq !== '0';\n}","typeGuard":"function isCanonicalDecimal(v: unknown, positive = false): v is string {\n  return typeof v === 'string' && /^(?:0|[1-9][0-9]{0,19})$/.test(v) && (!positive || v !== '0');\n}","tryCatchPattern":"const m = (sync.status().lastError ?? '').match(/malformed acked\\[(\\d+)\\]/);\nif (m) {\n  // index tells you which entry to inspect in the captured hub response; check field types and decimal form\n}","preventionTips":["Never pad or sign sequence/rev strings on the hub; emit String(n) directly","Reject seqs above uint64 at the hub append path","Reuse one shared canonical-decimal validator on hub and client so rules cannot drift"],"tags":["sync","hub","protocol-validation","ack","decimal-format"],"backgroundTag":"schema-validation-failed","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}