ruvnet/ruflo · error · Error

metric must be finite

Error message

metric must be finite

What it means

Thrown by the internal decimal() formatter when Number.isFinite(value) is false. decimal() converts numeric metrics (baselineScore, candidateScore, heldOutDeltas entries, lift, probabilities) into fixed-scale canonical strings for the receipt payload. NaN or Infinity cannot be represented as finite decimals and would corrupt the promotion statistics.

Source

Thrown at v3/@claude-flow/cli/src/services/flywheel-receipt.ts:207

}

export function policyCandidateId(policy: Record<string, unknown>): string {
  return sha256Ref(canonicalizeJcs(policy));
}

/** UUIDv7 with a 48-bit millisecond timestamp and RFC-4122 variant bits. */
export function uuidV7(now = Date.now()): string {
  const bytes = randomBytes(16);
  const timestamp = BigInt(now);
  for (let i = 5; i >= 0; i--) bytes[5 - i] = Number((timestamp >> BigInt(i * 8)) & 0xffn);
  bytes[6] = (bytes[6] & 0x0f) | 0x70;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;
  const hex = bytes.toString('hex');
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}

const decimal = (value: number, scale = 12): string => {
  if (!Number.isFinite(value)) throw new Error('metric must be finite');
  const normalized = value.toFixed(scale).replace(/\.?0+$/, '');
  return normalized === '-0' || normalized === '' ? '0' : normalized;
};

function seedFrom(parts: string[]): { seed: number; hex: string } {
  const digest = createHash('sha256').update(parts.join('')).digest();
  return { seed: digest.readUInt32BE(0), hex: digest.toString('hex') };
}

/**
 * Deterministic three-way quickselect. Bootstrap promotion needs one quantile,
 * not a fully sorted distribution; selecting it in expected O(n) removes the
 * O(n log n) sort from every evaluation. Three-way partitioning also keeps the
 * common all-equal bootstrap case linear.
 */
function selectKth(values: number[], k: number): number {
  let left = 0;
  let right = values.length - 1;

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Validate all scores with Number.isFinite() before receipt creation.
  2. Guard upstream divisions with Math.max(Math.abs(denom), epsilon) (the code already does this for relativeLift).
  3. Reject or default non-finite deltas at the evaluation boundary.
  4. Add unit tests asserting finite-only score arrays.

Example fix

// before
createFlywheelReceipt({ baselineScore: 0.42, candidateScore: NaN, heldOutDeltas: [...], /* ... */ });
// after
if (!Number.isFinite(candidateScore)) throw new Error('candidate score not finite');
createFlywheelReceipt({ baselineScore: 0.42, candidateScore, heldOutDeltas: [...], /* ... */ });
Defensive patterns

Strategy: validation

Validate before calling

function assertAllFinite(scores: number[], label: string): void {
  for (let i = 0; i < scores.length; i++) {
    if (!Number.isFinite(scores[i])) {
      throw new Error(`non-finite ${label}[${i}]: ${scores[i]}`);
    }
  }
}
assertAllFinite([baselineScore, candidateScore, ...heldOutDeltas], 'score');

Type guard

const isFiniteNumber = (x: unknown): x is number => typeof x === 'number' && Number.isFinite(x);

Try / catch

try {
  createFlywheelReceipt(input);
} catch (e) {
  if (e instanceof Error && e.message === 'metric must be finite') {
    throw new Error('evaluation produced non-finite scores; check upstream math for NaN/Infinity');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling createFlywheelReceipt() or computePromotionStatistics() where baselineScore, candidateScore, or any element of heldOutDeltas is NaN, Infinity, or -Infinity. Also reachable if a division elsewhere produced NaN and it propagated into a score.

Common situations: Score pipeline divides by zero yielding Infinity; a missing metric defaulted to NaN; heldOutDeltas computed from malformed paired outcomes; log/softmax underflow producing NaN.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/e37c3ba15bbd68ce. Report an issue: GitHub.