koala73/worldmonitor · error · TypeError

Unknown physical divergence freshness reason: ${String(reaso

Error message

Unknown physical divergence freshness reason: ${String(reason)}

What it means

physicalDivergenceStateForFreshnessReason maps a freshness-reason string to a divergence state ('missing_input' or 'stale_input') using fixed allow-lists (MISSING_REASONS / STALE_REASONS). Any reason string not on those lists throws a TypeError, since the physical-divergence contract only defines a closed set of reasons.

Source

Thrown at shared/physical-divergence-contract.js:115

    physicalStaleAfterCalendarDays: 12,
    paperMaxAgeMs: 36 * 60 * 60 * 1000,
    fxMaxAgeMs: 60 * 60 * 60 * 1000,
    missingReasons: MISSING_REASONS,
    staleReasons: STALE_REASONS,
  }),
  reasons: REASONS,
  storedReadingReasonsByState: STORED_READING_REASONS_BY_STATE,
  rpcFallbackReasons: RPC_FALLBACK_REASONS,
  readingReasonValues: READING_REASON_VALUES,
  compositeReasonValues: COMPOSITE_REASON_VALUES,
  readingReasonPattern: pattern(READING_REASON_VALUES),
  compositeReasonPattern: pattern(COMPOSITE_REASON_VALUES),
});

export function physicalDivergenceStateForFreshnessReason(reason) {
  if (MISSING_REASONS.includes(reason)) return 'missing_input';
  if (STALE_REASONS.includes(reason)) return 'stale_input';
  throw new TypeError(`Unknown physical divergence freshness reason: ${String(reason)}`);
}

export function isPhysicalDivergenceStoredReadingReason(state, reason) {
  return STORED_READING_REASONS_BY_STATE[state]?.includes(reason) === true;
}

export function isPhysicalDivergenceStoredCompositeReason(state, reason) {
  if (state === 'ok') return reason === '';
  return NON_OK_STATE_PRIORITY.includes(state)
    && COMPOSITE_MEMBER_REASONS.includes(reason)
    && reason.endsWith(`:${state}`);
}

export function buildPhysicalStressComposite(readings) {
  const values = Array.isArray(readings) ? readings : [];
  const byMetal = new Map();
  for (const reading of values) {
    if (!METAL_ORDER.includes(reading?.metal)) {

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Use the contract-exported reason constants instead of string literals
  2. Check the reason against MISSING_REASONS/STALE_REASONS (or the reason patterns) before calling
  3. Align producer and consumer to the same shared/physical-divergence-contract.js version
  4. Add the new reason to the contract deliberately and update both reason lists if it is a legitimate new state

Example fix

// before
const state = physicalDivergenceStateForFreshnessReason('stale-data');
// after
import { STALE_REASONS, physicalDivergenceStateForFreshnessReason } from './physical-divergence-contract.js';
const state = STALE_REASONS.includes('stale_data') ? physicalDivergenceStateForFreshnessReason('stale_data') : null;
Defensive patterns

Strategy: type-guard

Validate before calling

import { MISSING_REASONS, STALE_REASONS } from './physical-divergence-contract.js';
function isKnownFreshnessReason(reason) {
  return MISSING_REASONS.includes(reason) || STALE_REASONS.includes(reason);
}

Type guard

function isFreshnessReason(v) {
  return typeof v === 'string' &&
    (MISSING_REASONS.includes(v) || STALE_REASONS.includes(v));
}

Try / catch

try {
  state = physicalDivergenceStateForFreshnessReason(reason);
} catch (e) {
  if (e instanceof TypeError && e.message.startsWith('Unknown physical divergence freshness reason')) {
    state = null; // treat as unrecognized input, log the reason value
  } else throw e;
}

Prevention

When it happens

Trigger: Calling with a typo'd or renamed reason (e.g. 'stale-data' instead of a contract reason), a reason added by a newer producer but consumed by an older contract version, or passing null/undefined/non-string values.

Common situations: Contract version drift between producer and consumer; hand-written reason strings instead of the exported constants; refactor renaming a reason in one place only.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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