thedotmack/claude-mem · error · Error

canonical content: exceeds the local SQLite safe-integer…

Error message

canonical content: ${name} exceeds the local SQLite safe-integer range

What it means

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.

Solutions

  1. Convert the source timestamp unit (e.g. divide nanoseconds by 1e6) so the value fits in the safe-integer range before syncing.
  2. Check the server/system clock configuration for wildly wrong epoch values.
  3. If larger values are legitimate, persist them as strings instead of routing through canonicalDecimalToSafeInteger.

Example fix

// before
const ts = canonicalDecimalToSafeInteger(serverNanos, 'serverTs');
// after
const ts = canonicalDecimalToSafeInteger(BigInt(serverNanos) / 1000000n, 'serverTs'); // nanos -> millis
Defensive patterns

Strategy: type-guard

Validate before calling

const n = Number(value);
if (!Number.isSafeInteger(n)) {
  throw new Error(`${name} out of safe-integer range; convert units first`);
}

Type guard

function isSafeInt(v: unknown): v is number {
  return typeof v === 'number' && Number.isSafeInteger(v);
}

Try / catch

try {
  ts = canonicalDecimalToSafeInteger(rawServerTs, 'serverTs');
} catch (e) {
  if (e.message.includes('safe-integer')) { ts = normalizeTimestampUnit(rawServerTs); }
  else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/83d1704153a240db. Report an issue: GitHub.

Appendix: source

Thrown at src/services/sync/CanonicalContent.ts:138

export function compareCanonicalDecimals(left: string, right: string): -1 | 0 | 1 {
  assertCanonicalDecimal(left);
  assertCanonicalDecimal(right);
  if (left.length !== right.length) return left.length < right.length ? -1 : 1;
  if (left === right) return 0;
  return left < right ? -1 : 1;
}

export function incrementCanonicalDecimal(value: string): string {
  const canonical = assertCanonicalDecimal(value);
  if (BigInt(canonical) === UINT64_MAX) jsonError('uint64 sequence overflow');
  return (BigInt(canonical) + 1n).toString(10);
}

export function canonicalDecimalToSafeInteger(value: unknown, name: string): number {
  const canonical = assertCanonicalDecimal(value);
  const parsed = Number(canonical);
  if (!Number.isSafeInteger(parsed)) {
    throw new Error(`canonical content: ${name} exceeds the local SQLite safe-integer range`);
  }
  return parsed;
}

export function stableDocumentId(
  kind: ContentKind,
  originDeviceId: string,
  originLocalId: string,
): string {
  if (!CONTENT_KINDS.has(kind)) jsonError(`unsupported content kind ${String(kind)}`);
  const deviceId = assertDeviceId(originDeviceId);
  const localId = assertCanonicalDecimal(originLocalId);
  const input = canonicalJson([
    'cmem-doc-id-v1',
    'device',
    kind,
    deviceId,
    localId,

View on GitHub (pinned to d8bc9755e7)