nexu-io/open-design · error · RangeError

${key} must be a non-negative finite number, got "${raw}"

Error message

${key} must be a non-negative finite number, got "${raw}"

What it means

Thrown by parseNonNegativeFloat() (used by loadCritiqueConfigFromEnv for OD_CRITIQUE_SCORE_THRESHOLD) as a RangeError when the value is set but is not a finite number >= 0 (negative, NaN, Infinity, or non-numeric). ${key} is always 'OD_CRITIQUE_SCORE_THRESHOLD' in current usage; ${raw} is the offending input. Threshold is a float because panel scores are continuous.

Source

Thrown at apps/daemon/src/critique/config.ts:69

  return v === 'true' || v === '1' || v === 'yes';
}

function parsePositiveInt(key: string, raw: string | undefined, fallback: number): number {
  if (raw === undefined) return fallback;
  const n = Number(raw);
  if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
    throw new RangeError(
      `${key} must be a positive integer, got "${raw}"`,
    );
  }
  return n;
}

function parseNonNegativeFloat(key: string, raw: string | undefined, fallback: number): number {
  if (raw === undefined) return fallback;
  const n = Number(raw);
  if (!Number.isFinite(n) || n < 0) {
    throw new RangeError(
      `${key} must be a non-negative finite number, got "${raw}"`,
    );
  }
  return n;
}

function parseFallbackPolicy(
  raw: string | undefined,
  fallback: CritiqueConfig['fallbackPolicy'],
): CritiqueConfig['fallbackPolicy'] {
  if (raw === undefined) return fallback;
  const trimmed = raw.trim();
  if (FALLBACK_POLICIES.includes(trimmed as CritiqueConfig['fallbackPolicy'])) {
    return trimmed as CritiqueConfig['fallbackPolicy'];
  }
  throw new RangeError(
    `OD_CRITIQUE_FALLBACK_POLICY must be one of ${FALLBACK_POLICIES.join(', ')}, got "${raw}"`,
  );

View on GitHub (pinned to 5be4028344)

Solutions

  1. Set OD_CRITIQUE_SCORE_THRESHOLD to a non-negative finite number (e.g. 0.8, or 8 depending on scale) using a dot decimal.
  2. Unset it to use the default threshold from defaultCritiqueConfig().
  3. Ensure templated env files emit a plain numeric literal, not empty strings or localized decimals.

Example fix

# before: negative / non-numeric / locale decimal
OD_CRITIQUE_SCORE_THRESHOLD=-1
OD_CRITIQUE_SCORE_THRESHOLD=0,8

# after: non-negative finite, dot decimal
OD_CRITIQUE_SCORE_THRESHOLD=0.8
Defensive patterns

Strategy: validation

Validate before calling

function assertNonNegFloat(key: string, raw: string | undefined, fallback: number) {
  if (raw === undefined) return fallback;
  const n = Number(raw);
  if (!Number.isFinite(n) || n < 0) {
    throw new RangeError(`${key} must be a non-negative finite number, got "${raw}"`);
  }
  return n;
}

Type guard

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

Try / catch

try { loadCritiqueConfigFromEnv(); }
catch (e) {
  if (e instanceof RangeError && /non-negative/.test(e.message)) {
    // fall back to defaults or fail boot
  } else throw e;
}

Prevention

When it happens

Trigger: Setting OD_CRITIQUE_SCORE_THRESHOLD to a negative number, NaN, Infinity, or a non-numeric string. e.g. OD_CRITIQUE_SCORE_THRESHOLD=-1, OD_CRITIQUE_SCORE_THRESHOLD=high.

Common situations: Operator entered a negative threshold thinking it disables the gate; a templated value injected an empty string or 'auto'; locale-related comma decimal ('0,8') parsed to NaN.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/0a7ae17c42718f03. Report an issue: GitHub.