koala73/worldmonitor · error · TypeError

Physical divergence evaluation clock is invalid

Error message

Physical divergence evaluation clock is invalid

What it means

applyFreshness() stamps each reading with staleness computed against a wall-clock timestamp nowMs. If nowMs is not a finite number (NaN, Infinity, undefined coerced oddly, a string) it throws this TypeError immediately, because staleness decisions against an invalid clock would be meaningless.

Source

Thrown at server/_shared/physical-divergence-snapshot.ts:381

  methodology(raw.methodologyVersion);
  canonicalWeights(raw.weights);
  const expected = buildPhysicalStressComposite(readings);
  const actualState = state(raw.state);
  const actualIndex = nullableFinite(raw.index);
  if (
    !string(raw.reason)
    || !isPhysicalDivergenceStoredCompositeReason(actualState, raw.reason)
    || actualState !== expected.state
    || raw.reason !== expected.reason
    || actualIndex !== expected.index
  ) throw new TypeError('Physical divergence composite does not match its member readings');
}

function applyFreshness(
  readings: PhysicalDivergenceRawReading[],
  nowMs: number,
): PhysicalDivergenceRawReading[] {
  if (!Number.isFinite(nowMs)) throw new TypeError('Physical divergence evaluation clock is invalid');
  return readings.map((entry) => {
    if (entry.state !== 'ok' && entry.state !== 'insufficient_history') return entry;
    const staleReason = physicalDivergenceStaleReason({
      physicalAsOf: entry.physicalAsOf,
      paperAsOf: entry.paperAsOf,
      fxAsOf: entry.provenance.fxAsOf,
    }, nowMs);
    if (!staleReason) return entry;
    return {
      ...entry,
      state: physicalDivergenceStateForFreshnessReason(staleReason),
      reason: staleReason,
      regime: null,
      index: null,
      percentile: null,
      robustZ: null,
      delta5d: null,
      delta20d: null,

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Pass a finite epoch-milliseconds number: Number.isFinite(nowMs) must hold — use Date.now() by default.
  2. If the clock comes from config/tests, coerce and validate it first: const t = Number(raw); if (!Number.isFinite(t)) throw ...
  3. Fix test fakes to return a finite number from the clock function.
  4. If a timestamp string is the source, parse it with Date.parse(...) and check for NaN before passing.

Example fix

// before
applyFreshness(readings, config.evaluationClock); // string
// after
const nowMs = Number(config.evaluationClock);
if (!Number.isFinite(nowMs)) nowMs = Date.now();
applyFreshness(readings, nowMs);
Defensive patterns

Strategy: validation

Validate before calling

const nowMs = clock?.() ?? Date.now();
if (!Number.isFinite(nowMs)) throw new Error(`evaluation clock must be finite epoch ms, got ${nowMs}`);

Type guard

function isFiniteEpochMs(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v > 0;
}

Try / catch

try {
  applyFreshness(readings, nowMs);
} catch (err) {
  if (err instanceof TypeError && err.message.includes('evaluation clock is invalid')) {
    applyFreshness(readings, Date.now()); // fall back to wall clock
  } else throw err;
}

Prevention

When it happens

Trigger: Calling applyFreshness (via the snapshot normalization pipeline: readings -> normalizePhysicalDivergenceSnapshot -> getPhysicalDivergenceIndex) with nowMs that is NaN, Infinity, undefined-as-NaN, or a non-numeric type — e.g. Date.now() replaced by an unparseable injected clock in tests or a corrupted config value.

Common situations: Tests injecting a fake clock that returns undefined; configuration supplying a timestamp string ('2026-09-01T...') instead of epoch ms; arithmetic on a missing field producing NaN; Date.now() monkey-patched incorrectly.

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/a1d47a1487553ee6. Report an issue: GitHub.