{"record":{"id":"5e5de6fdae57ba10","repo":"actualbudget/actual","slug":"timestamp-overflowerror","errorCode":null,"errorMessage":"Timestamp.OverflowError","messagePattern":"Timestamp\\.OverflowError","errorType":"exception","errorClass":"Timestamp.OverflowError","httpStatus":null,"severity":"error","filePath":"packages/crdt/src/crdt/timestamp.ts","lineNumber":218,"sourceCode":"    // retrieve the local wall time\n    const phys = Date.now();\n\n    // unpack the clock.timestamp logical time and counter\n    const lOld = clock.timestamp.millis();\n    const cOld = clock.timestamp.counter();\n\n    // calculate the next logical time and counter\n    // * ensure that the logical time never goes backward\n    // * increment the counter if phys time does not advance\n    const lNew = Math.max(lOld, phys);\n    const cNew = lOld === lNew ? cOld + 1 : 0;\n\n    // check the result for drift and counter overflow\n    if (lNew - phys > config.maxDrift) {\n      throw new Timestamp.ClockDriftError(lNew, phys, config.maxDrift);\n    }\n    if (cNew > MAX_COUNTER) {\n      throw new Timestamp.OverflowError();\n    }\n\n    // repack the logical time/counter\n    clock.timestamp.setMillis(lNew);\n    clock.timestamp.setCounter(cNew);\n\n    return new Timestamp(\n      clock.timestamp.millis(),\n      clock.timestamp.counter(),\n      clock.timestamp.node(),\n    );\n  }\n\n  // Timestamp receive. Parses and merges a timestamp from a remote\n  // system with the local timeglobal uniqueness and monotonicity are\n  // preserved\n  static recv(msg: Timestamp): Timestamp | null {\n    if (!clock) {","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/crdt/src/crdt/timestamp.ts#L200-L236","documentation":"Timestamp.OverflowError is thrown by the hybrid logical clock when the per-millisecond counter would exceed MAX_COUNTER (65535) in Timestamp.send(). It means too many timestamps were generated within the same logical millisecond, so the clock cannot guarantee monotonic unique IDs. It signals internal counter exhaustion, not a user-facing condition.","triggerScenarios":"Calling Timestamp.send() more than 65535 times while the local logical time (lOld, possibly advanced ahead of wall time by drift) stays equal to Date.now(), so cNew = cOld + 1 exceeds MAX_COUNTER.","commonSituations":"Bulk-importing or syncing tens of thousands of entities in a tight loop on a fast machine whose clock has drifted forward (lOld pinned to max(lOld, phys)); long-running client generating many mutations in one millisecond window.","solutions":["Reduce the rate of Timestamp.send() calls (batch/yield with await so wall time advances)","Check and fix the local clock drift so lNew tracks Date.now() and the counter resets each new millisecond","Reinitialize the clock (Timestamp.init clock) after fixing time, ensuring logical time is not stuck ahead","Catch the error and back off/retry after letting the wall clock advance past lOld"],"exampleFix":"// before\ntimestamps = items.map(() => Timestamp.send());\n// after\nconst timestamps = [];\nfor (const item of items) {\n  let ts = Timestamp.send();\n  if (!ts) { await new Promise(r => setTimeout(r, 2)); ts = Timestamp.send(); }\n  if (!ts) throw new Error('Timestamp clock exhausted');\n  timestamps.push(ts);\n}","handlingStrategy":"try-catch","validationCode":"// Rate-limit sends and ensure the clock is initialized\nif (!Timestamp.send()) throw new Error('Clock not initialized');\n// Optionally pre-check headroom:\n// counter resets each new millisecond, so ensure calls per ms < 65535","typeGuard":null,"tryCatchPattern":"try {\n  const ts = Timestamp.send();\n} catch (e) {\n  if (e instanceof Timestamp.OverflowError) {\n    await new Promise(r => setTimeout(r, 2)); // let wall time advance\n    const ts = Timestamp.send();\n  } else throw e;\n}","preventionTips":["Never call Timestamp.send() in an unbounded tight loop; yield to the event loop periodically","Keep the system clock accurate via NTP so the counter resets on each new millisecond","Initialize the clock once and monitor for persistent drift","Cap batch sizes when generating large numbers of entities"],"tags":["crdt","clock","overflow","hybrid-logical-clock"],"backgroundTag":"hlc-counter-overflow","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}