{"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":"exception","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/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/sync/CloudSync.ts#L983-L1019","documentation":"Thrown when an acked entry IS a plain object but one or more required fields have the wrong type. Required: `id`, `kind`, `entity_rev`, `operation_sha256`, `seq` must be strings; `origin_local_id` must be either null or a string. This is the deeper field-level shape check that runs after [120] passes and before the canonical-decimal assertions on entity_rev/seq/origin_local_id.","triggerScenarios":"A 200 push response whose acked object has a non-string field, e.g. `entity_rev` as a number, `origin_local_id` as `0`, or `seq` missing/undefined. Fires per-index inside `acked.map`, so the index in the message identifies which entry is malformed.","commonSituations":"Hub serializes numeric rev/seq fields as JSON numbers instead of decimal strings (the contract requires canonical decimal strings), `origin_local_id` emitted as `0`/empty rather than null, or a field renamed/omitted after a hub schema change. Also from a hand-crafted mock response missing a field.","solutions":["Read the acked[index] object from the hub response and verify each of id/kind/entity_rev/operation_sha256/seq is a string and origin_local_id is null-or-string.","If the hub is emitting numeric rev/seq, fix the hub to emit canonical decimal strings (e.g. \"5\" not 5) — the assertCanonicalDecimal calls immediately after this check require strings.","Align hub and client versions so the AckedOp wire schema matches.","Replay the failing push with response logging to capture the exact offending field and index."],"exampleFix":"// before: { \"entity_rev\": 5, \"seq\": 12, \"origin_local_id\": 0, ... }\n// after:  { \"entity_rev\": \"5\", \"seq\": \"12\", \"origin_local_id\": null, ... }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isAckedOp(v: unknown): v is AckedOp {\n  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n  const o = v as Record<string, unknown>;\n  return typeof o.id === 'string'\n    && typeof o.kind === 'string'\n    && (o.origin_local_id === null || typeof o.origin_local_id === 'string')\n    && typeof o.entity_rev === 'string'\n    && typeof o.operation_sha256 === 'string'\n    && typeof o.seq === 'string';\n}","tryCatchPattern":"try { await cloudSync.push(pushed); }\ncatch (e) {\n  if (e instanceof Error && e.message.includes('malformed acked')) {\n    // hub field-type regression; surface but leave batch un-stamped\n    logger.error('SYNC', e.message); return;\n  }\n  throw e;\n}","preventionTips":["Require the hub to emit rev/seq as canonical decimal strings, not JSON numbers.","Add an integration test that asserts every hub ack field type against AckedOp.","Keep the assertCanonicalDecimal positive-option behavior aligned between client and hub tests."],"tags":["sync","cloud-sync","hub-response","validation","wire-contract"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}