affaan-m/ECC · error · Error

timeout is outside the 10-120 second safety range

Error message

timeout is outside the 10-120 second safety range

What it means

runReview clamps the Codex subprocess timeout to the closed interval [10_000, MAX_TIMEOUT_MS] ms, i.e. 10s to 120s. Values outside that range are rejected so the review neither fails from an impossibly short budget nor hangs unbounded. The CLI parseArgs already validates --timeout-seconds as an integer 10-120, but runReview re-checks the millisecond value to protect programmatic callers.

Source

Thrown at skills/council-multi-model/scripts/review-with-codex.js:188

function buildEnvironment(sourceEnv = process.env) {
  const allowed = [
    'PATH', 'HOME', 'USERPROFILE', 'CODEX_HOME',
    'TMPDIR', 'TMP', 'TEMP', 'SystemRoot', 'ComSpec', 'PATHEXT',
  ];
  return Object.fromEntries(
    allowed.filter((name) => sourceEnv[name]).map((name) => [name, sourceEnv[name]])
  );
}

function runReview(prompt, options, dependencies = {}) {
  if (!prompt.trim()) throw new Error('review packet is empty');
  if (Buffer.byteLength(prompt, 'utf8') > MAX_PROMPT_BYTES) {
    throw new Error(`review packet exceeds ${MAX_PROMPT_BYTES} bytes`);
  }
  if (!options.consent) throw new Error('OpenAI transfer consent is required');
  if (options.timeoutMs < 10_000 || options.timeoutMs > MAX_TIMEOUT_MS) {
    throw new Error('timeout is outside the 10-120 second safety range');
  }

  const spawn = dependencies.spawnSync || spawnSync;
  const environment = buildEnvironment(dependencies.env || process.env);
  const verifySupport = dependencies.verifyToollessSupport || verifyToollessSupport;
  verifySupport({ spawnSync: spawn, env: environment });
  const makeTemp = dependencies.mkdtempSync || fs.mkdtempSync;
  const readFile = dependencies.readFileSync || fs.readFileSync;
  const remove = dependencies.rmSync || fs.rmSync;
  const tempDir = makeTemp(path.join(os.tmpdir(), 'ecc-council-review-'));
  const outputFile = path.join(tempDir, 'last-message.txt');

  try {
    const result = spawn('codex', buildCodexArgs(tempDir, outputFile), {
      cwd: tempDir,
      env: environment,
      input: prompt,
      encoding: 'utf8',

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Set options.timeoutMs to a value between 10000 and 120000 (inclusive).
  2. If your config uses seconds, multiply by 1000 before passing: options.timeoutMs = seconds * 1000.
  3. Leave timeoutMs unset to accept the 60_000 default.

Example fix

// before (seconds passed as ms)
runReview(packet, { consent: true, timeoutMs: 60, hostProvider: 'openai' });

// after
runReview(packet, { consent: true, timeoutMs: 60_000, hostProvider: 'openai' });
Defensive patterns

Strategy: validation

Validate before calling

const MIN_TIMEOUT_MS = 10_000;
const MAX_TIMEOUT_MS = 120_000;
function clampTimeout(ms) {
  if (!Number.isFinite(ms) || ms < MIN_TIMEOUT_MS || ms > MAX_TIMEOUT_MS) {
    throw new Error(`timeoutMs must be within [${MIN_TIMEOUT_MS}, ${MAX_TIMEOUT_MS}]`);
  }
  return ms;
}
options.timeoutMs = clampTimeout(options.timeoutMs ?? 60_000);

Prevention

When it happens

Trigger: Calling runReview with options.timeoutMs < 10000 or options.timeoutMs > 120000. Common root cause: passing seconds (e.g. 60) instead of milliseconds (60000), or passing 0.

Common situations: Treating timeoutMs as seconds; passing timeout: 0 meaning 'no timeout'; unit tests using a tiny timeout to force fast failure; copying a value from a config that used seconds.

Understand the failure class

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/a96f334bd756a5a1. Report an issue: GitHub.