nexu-io/open-design · error · RangeError

${key} must be a positive integer, got "${raw}"

Error message

${key} must be a positive integer, got "${raw}"

What it means

Thrown by parsePositiveInt() (used by loadCritiqueConfigFromEnv) as a RangeError when one of the OD_CRITIQUE_* integer env vars (OD_CRITIQUE_MAX_ROUNDS, OD_CRITIQUE_SCORE_SCALE, OD_CRITIQUE_PER_ROUND_TIMEOUT_MS, OD_CRITIQUE_TOTAL_TIMEOUT_MS, OD_CRITIQUE_PARSER_MAX_BLOCK_BYTES) is set but is not a finite integer >= 1. ${key} names exactly which env var failed and ${raw} shows the offending value.

Source

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

    fallbackPolicy,
  };
}

// ---------------------------------------------------------------------------
// Parsing helpers
// ---------------------------------------------------------------------------

function parseEnabled(raw: string | undefined, fallback: boolean): boolean {
  if (raw === undefined) return fallback;
  const v = raw.trim().toLowerCase();
  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(

View on GitHub (pinned to 5be4028344)

Solutions

  1. Set the named var to a positive integer (>= 1) with no units — e.g. OD_CRITIQUE_MAX_ROUNDS=3, OD_CRITIQUE_PER_ROUND_TIMEOUT_MS=60000.
  2. Unset the var to fall back to defaultCritiqueConfig().
  3. Strip units/whitespace from templated env values before they reach the process.

Example fix

# before: zero / float / non-numeric
OD_CRITIQUE_MAX_ROUNDS=0
OD_CRITIQUE_PER_ROUND_TIMEOUT_MS=2.5s

# after: positive integers, no units
OD_CRITIQUE_MAX_ROUNDS=3
OD_CRITIQUE_PER_ROUND_TIMEOUT_MS=60000
Defensive patterns

Strategy: validation

Validate before calling

function assertPositiveInt(key: string, raw: string | undefined, fallback: 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;
}

Type guard

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

Try / catch

try { loadCritiqueConfigFromEnv(); }
catch (e) {
  if (e instanceof RangeError) { console.error('critique config invalid', e.message); process.exit(1); }
  throw e;
}

Prevention

When it happens

Trigger: Setting any of the listed OD_CRITIQUE_* integer vars to zero, a negative, a float, NaN/Infinity, or a non-numeric string. e.g. OD_CRITIQUE_MAX_ROUNDS=0, OD_CRITIQUE_PER_ROUND_TIMEOUT_MS=2.5, OD_CRITIQUE_TOTAL_TIMEOUT_MS=abc.

Common situations: Operator set a timeout to 0 thinking it disables it; trailing whitespace/unit ('1000ms'); a float slipped in via a templated env file; copy-paste from a doc that used a different unit.

Related errors


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