{"record":{"id":"83d1704153a240db","repo":"thedotmack/claude-mem","slug":"canonical-content-name-exceeds-the-local-sqlit","errorCode":null,"errorMessage":"canonical content: ${name} exceeds the local SQLite safe-integer range","messagePattern":"canonical content: (.+?) exceeds the local SQLite safe-integer range","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/CanonicalContent.ts","lineNumber":138,"sourceCode":"export function compareCanonicalDecimals(left: string, right: string): -1 | 0 | 1 {\n  assertCanonicalDecimal(left);\n  assertCanonicalDecimal(right);\n  if (left.length !== right.length) return left.length < right.length ? -1 : 1;\n  if (left === right) return 0;\n  return left < right ? -1 : 1;\n}\n\nexport function incrementCanonicalDecimal(value: string): string {\n  const canonical = assertCanonicalDecimal(value);\n  if (BigInt(canonical) === UINT64_MAX) jsonError('uint64 sequence overflow');\n  return (BigInt(canonical) + 1n).toString(10);\n}\n\nexport function canonicalDecimalToSafeInteger(value: unknown, name: string): number {\n  const canonical = assertCanonicalDecimal(value);\n  const parsed = Number(canonical);\n  if (!Number.isSafeInteger(parsed)) {\n    throw new Error(`canonical content: ${name} exceeds the local SQLite safe-integer range`);\n  }\n  return parsed;\n}\n\nexport function stableDocumentId(\n  kind: ContentKind,\n  originDeviceId: string,\n  originLocalId: string,\n): string {\n  if (!CONTENT_KINDS.has(kind)) jsonError(`unsupported content kind ${String(kind)}`);\n  const deviceId = assertDeviceId(originDeviceId);\n  const localId = assertCanonicalDecimal(originLocalId);\n  const input = canonicalJson([\n    'cmem-doc-id-v1',\n    'device',\n    kind,\n    deviceId,\n    localId,","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/CanonicalContent.ts#L120-L156","documentation":"canonicalDecimalToSafeInteger converts a canonical decimal value (e.g. a server timestamp) into a JS number for storage in SQLite. Because SQLite/JS only guarantee safe integers up to 2^53-1, it throws when Number(canonical) is not a safe integer, preventing silent precision loss.","triggerScenarios":"Calling canonicalDecimalToSafeInteger (or its callers serverTs / localPayload) with a decimal whose numeric value is outside Number.MIN_SAFE_INTEGER..Number.MAX_SAFE_INTEGER, e.g. a microsecond/nanosecond-precision timestamp or a server clock producing values > 9007199254740991.","commonSituations":"A server emitting timestamps in nanoseconds or microseconds while the client expects milliseconds; a remote clock misconfigured to epoch-far-future values; importing records from a system using larger numeric ids/timestamps.","solutions":["Convert the source timestamp unit (e.g. divide nanoseconds by 1e6) so the value fits in the safe-integer range before syncing.","Check the server/system clock configuration for wildly wrong epoch values.","If larger values are legitimate, persist them as strings instead of routing through canonicalDecimalToSafeInteger."],"exampleFix":"// before\nconst ts = canonicalDecimalToSafeInteger(serverNanos, 'serverTs');\n// after\nconst ts = canonicalDecimalToSafeInteger(BigInt(serverNanos) / 1000000n, 'serverTs'); // nanos -> millis","handlingStrategy":"type-guard","validationCode":"const n = Number(value);\nif (!Number.isSafeInteger(n)) {\n  throw new Error(`${name} out of safe-integer range; convert units first`);\n}","typeGuard":"function isSafeInt(v: unknown): v is number {\n  return typeof v === 'number' && Number.isSafeInteger(v);\n}","tryCatchPattern":"try {\n  ts = canonicalDecimalToSafeInteger(rawServerTs, 'serverTs');\n} catch (e) {\n  if (e.message.includes('safe-integer')) { ts = normalizeTimestampUnit(rawServerTs); }\n  else throw e;\n}","preventionTips":["Normalize timestamp units (nanos/micros -> millis) before sync.","Validate server clock configuration; absurd epochs signal misconfiguration.","Store oversized numerics as strings rather than forcing them into SQLite integers."],"tags":["numeric","sqlite","overflow"],"backgroundTag":"value-out-of-range","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}