ruvnet/RuView · error · RangeError

timeoutMs must be a safe integer between 1000 and 1800000

Error message

timeoutMs must be a safe integer between 1000 and 1800000

What it means

runProcess() enforces timeoutMs as a safe integer within the inclusive range [1000, 1800000] milliseconds (1 second to 30 minutes) so children can never be spawned with zero, fractional, or effectively unbounded timeouts. Out-of-range values, NaN, Infinity, or non-numbers throw this RangeError synchronously.

Source

Thrown at harness/homecore/src/process-runner.js:103

  force.unref();
  return force;
}

export function runProcess(command, args = [], {
  cwd,
  input = '',
  timeoutMs = 120_000,
  signal,
  maxOutputBytes = 1_048_576,
  env = process.env,
  envAllowlist = DEFAULT_ENV_ALLOWLIST,
} = {}) {
  if (!command || typeof command !== 'string') throw new TypeError('command must be a non-empty string');
  if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string')) {
    throw new TypeError('args must be an array of strings');
  }
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 1_800_000) {
    throw new RangeError('timeoutMs must be a safe integer between 1000 and 1800000');
  }
  if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1) {
    throw new RangeError('maxOutputBytes must be a positive safe integer');
  }
  const childEnv = scrubEnvironment(env, envAllowlist);

  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      cwd,
      env: childEnv,
      detached: process.platform !== 'win32',
      shell: false,
      windowsHide: true,
      stdio: ['pipe', 'pipe', 'pipe'],
    });
    const stdout = [];
    const stderr = [];
    let outputBytes = 0;

View on GitHub (pinned to 4685618388)

Solutions

  1. Pass milliseconds between 1000 and 1800000; there is intentionally no 'no timeout' mode
  2. Clamp user-supplied config before calling: Math.min(Math.max(Number.isSafeInteger(v) ? v : 120000, 1000), 1800000)
  3. Assert Number.isSafeInteger(timeoutMs) whenever the value is computed rather than literal

Example fix

// before
await runProcess('cargo', ['test'], { timeoutMs: 90 }); // 90 seconds intended, 90ms given

// after
await runProcess('cargo', ['test'], { timeoutMs: 90_000 });
Defensive patterns

Strategy: validation

Validate before calling

function normalizeTimeoutMs(value, fallback = 120_000) {
  const n = Number.isSafeInteger(value) ? value : fallback;
  return Math.min(Math.max(n, 1_000), 1_800_000);
}
// use: runProcess(cmd, args, { timeoutMs: normalizeTimeoutMs(userConfig.timeout) })

Type guard

/** @param {unknown} v @returns {v is number} */
function isValidTimeoutMs(v) {
  return Number.isSafeInteger(v) && v >= 1_000 && v <= 1_800_000;
}

Try / catch

try {
  await runProcess(cmd, args, { timeoutMs });
} catch (error) {
  if (error instanceof RangeError && error.message.includes('timeoutMs')) {
    throw new Error(`timeoutMs=${timeoutMs} is outside [1000, 1800000] ms`);
  }
  throw error;
}

Prevention

When it happens

Trigger: {timeoutMs: 0} attempting 'no timeout', {timeoutMs: 30} passing seconds instead of milliseconds, {timeoutMs: 2_400_000} exceeding the 30-minute ceiling, {timeoutMs: 30_000.5} fractional, NaN propagated from arithmetic on undefined, or a string '30000' read from env/config.

Common situations: Unit confusion between seconds and milliseconds, forwarding unvalidated user config, NaN leaking from computations like undefined * 1000.

Understand the failure class

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/166a9700eec5f6a3. Report an issue: GitHub.