{"record":{"id":"166a9700eec5f6a3","repo":"ruvnet/RuView","slug":"timeoutms-must-be-a-safe-integer-between-1000-and","errorCode":null,"errorMessage":"timeoutMs must be a safe integer between 1000 and 1800000","messagePattern":"timeoutMs must be a safe integer between 1000 and 1800000","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"harness/homecore/src/process-runner.js","lineNumber":103,"sourceCode":"  force.unref();\n  return force;\n}\n\nexport function runProcess(command, args = [], {\n  cwd,\n  input = '',\n  timeoutMs = 120_000,\n  signal,\n  maxOutputBytes = 1_048_576,\n  env = process.env,\n  envAllowlist = DEFAULT_ENV_ALLOWLIST,\n} = {}) {\n  if (!command || typeof command !== 'string') throw new TypeError('command must be a non-empty string');\n  if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string')) {\n    throw new TypeError('args must be an array of strings');\n  }\n  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 1_800_000) {\n    throw new RangeError('timeoutMs must be a safe integer between 1000 and 1800000');\n  }\n  if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1) {\n    throw new RangeError('maxOutputBytes must be a positive safe integer');\n  }\n  const childEnv = scrubEnvironment(env, envAllowlist);\n\n  return new Promise((resolve, reject) => {\n    const child = spawn(command, args, {\n      cwd,\n      env: childEnv,\n      detached: process.platform !== 'win32',\n      shell: false,\n      windowsHide: true,\n      stdio: ['pipe', 'pipe', 'pipe'],\n    });\n    const stdout = [];\n    const stderr = [];\n    let outputBytes = 0;","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/harness/homecore/src/process-runner.js#L85-L121","documentation":"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.","triggerScenarios":"{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.","commonSituations":"Unit confusion between seconds and milliseconds, forwarding unvalidated user config, NaN leaking from computations like undefined * 1000.","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"],"exampleFix":"// before\nawait runProcess('cargo', ['test'], { timeoutMs: 90 }); // 90 seconds intended, 90ms given\n\n// after\nawait runProcess('cargo', ['test'], { timeoutMs: 90_000 });","handlingStrategy":"validation","validationCode":"function normalizeTimeoutMs(value, fallback = 120_000) {\n  const n = Number.isSafeInteger(value) ? value : fallback;\n  return Math.min(Math.max(n, 1_000), 1_800_000);\n}\n// use: runProcess(cmd, args, { timeoutMs: normalizeTimeoutMs(userConfig.timeout) })","typeGuard":"/** @param {unknown} v @returns {v is number} */\nfunction isValidTimeoutMs(v) {\n  return Number.isSafeInteger(v) && v >= 1_000 && v <= 1_800_000;\n}","tryCatchPattern":"try {\n  await runProcess(cmd, args, { timeoutMs });\n} catch (error) {\n  if (error instanceof RangeError && error.message.includes('timeoutMs')) {\n    throw new Error(`timeoutMs=${timeoutMs} is outside [1000, 1800000] ms`);\n  }\n  throw error;\n}","preventionTips":["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"],"tags":["validation","process","rangeerror","timeout"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}