{"record":{"id":"2a063e722421c478","repo":"ruvnet/ruflo","slug":"hlc-skew-exceeded-received-physicalms-receivedp","errorCode":null,"errorMessage":"HLC skew exceeded: received physicalMs=${receivedPhysicalMs} vs local=${localPhysicalMs} (max=${maxSkewMs}ms)","messagePattern":"HLC skew exceeded: received physicalMs=(.+?) vs local=(.+?) \\(max=(.+?)ms\\)","errorType":"exception","errorClass":"HlcSkewError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/infrastructure/hlc.ts","lineNumber":142,"sourceCode":"      logical = 0;\n    } else {\n      // Wall clock didn't advance (or went backward); keep last physical and bump logical.\n      physicalMs = this.last.physicalMs;\n      logical = this.last.logical + 1;\n    }\n\n    this.last = { physicalMs, logical, nodeId: this.nodeId };\n    return this.last;\n  }\n\n  update(received: HlcTimestamp): HlcTimestamp {\n    const wall = this.physicalClock();\n\n    // Skew guard: refuse HLCs that are too far in the future.\n    // We DO NOT jump local clock forward to match — a misbehaving peer would\n    // poison the global timeline. Instead we throw and let the caller decide.\n    if (received.physicalMs > wall + this.maxSkewMs) {\n      throw new HlcSkewError(received.physicalMs, wall, this.maxSkewMs);\n    }\n\n    const maxPhysical = Math.max(wall, this.last.physicalMs, received.physicalMs);\n\n    let logical: number;\n    if (maxPhysical === this.last.physicalMs && maxPhysical === received.physicalMs) {\n      logical = Math.max(this.last.logical, received.logical) + 1;\n    } else if (maxPhysical === this.last.physicalMs) {\n      logical = this.last.logical + 1;\n    } else if (maxPhysical === received.physicalMs) {\n      logical = received.logical + 1;\n    } else {\n      logical = 0;\n    }\n\n    this.last = { physicalMs: maxPhysical, logical, nodeId: this.nodeId };\n    return this.last;\n  }","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/claims/src/infrastructure/hlc.ts#L124-L160","documentation":"Hlc.update(received) rejects any remote timestamp whose physicalMs is more than maxSkewMs ahead of the local wall clock. The guard is deliberate: adopting a future timestamp would poison the hybrid logical clock's timeline for every participant, so it throws HlcSkewError and never jumps the local clock forward. The error message carries received vs local physicalMs plus the allowed skew so the offending node can be identified.","triggerScenarios":"applyRemoteEvent (or a direct hlc.update call) with a remote HLC from a node whose system clock runs fast: no NTP, a VM resumed from suspend, a container with skewed time, or replayed recorded events whose timestamps now sit far in the future relative to a reinitialized local clock; maxSkewMs configured smaller than real drift.","commonSituations":"Cloud VMs/containers without chrony/ntpd; laptop sleep/wake cycles during federated tests; CI runners with drifted clocks; cross-datacenter replication where one site's clock wanders.","solutions":["Sync clocks on every node: run chrony/ntpd/systemd-timesyncd and verify with timedatectl or chronyc tracking","Catch HlcSkewError around applyRemoteEvent, quarantine the offending event (or retry after clocks resync) instead of letting it kill the sync loop","If drift is legitimate and measured, raise maxSkewMs on the HLC configuration","Fix the source node identified by the received-vs-local values in the message; do not compensate by forwarding local time"],"exampleFix":"// before\nawait store.applyRemoteEvent(payload.event, payload.vclock, payload.hlc);\n\n// after\nimport { HlcSkewError } from '../infrastructure/hlc.js';\ntry {\n  await store.applyRemoteEvent(payload.event, payload.vclock, payload.hlc);\n} catch (e) {\n  if (e instanceof HlcSkewError) {\n    deadLetter.push({ event: payload.event, reason: 'clock-skew', detail: e.message });\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"const drift = payload.hlc.physicalMs - Date.now();\nif (drift > MAX_EXPECTED_SKEW_MS) {\n  deadLetter.push({ event: payload.event, reason: 'future-dated hlc', driftMs: drift });\n} else {\n  await store.applyRemoteEvent(payload.event, payload.vclock, payload.hlc);\n}","typeGuard":null,"tryCatchPattern":"import { HlcSkewError } from './hlc.js';\ntry { await store.applyRemoteEvent(ev, vclock, hlc); }\ncatch (e) {\n  if (e instanceof HlcSkewError) { deadLetter.push({ ev, reason: e.message }); return; }\n  throw e;\n}","preventionTips":["Run NTP/chrony on every federated node and alert on drift >1s","Never forward-jump the local HLC or wall clock to match a peer","Set maxSkewMs from measured worst-case drift, and quarantine (not crash) on violation","For event replay, strip old hlc timestamps or replay through a fresh clock path"],"tags":["distributed-systems","clock-skew","hlc","federation","typescript"],"backgroundTag":"clock-skew-detected","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}