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
- Pass milliseconds between 1000 and 1800000; there is intentionally no 'no timeout' mode
- Clamp user-supplied config before calling: Math.min(Math.max(Number.isSafeInteger(v) ? v : 120000, 1000), 1800000)
- 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
- Standardize on milliseconds everywhere in config; document the unit next to every timeout field
- Clamp external input to the allowed range instead of passing it through
- There is deliberately no zero/unlimited timeout — budget for the 30-minute ceiling in long jobs
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- maxOutputBytes must be a positive safe integer
- command must be a non-empty string
- args must be an array of strings
- Unsupported verification profile: ${profile}
- unsupported guidance topic: ${topic}
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/166a9700eec5f6a3.
Report an issue: GitHub.