affaan-m/ECC · error · Error

ETIMEDOUT

ETIMEDOUT

Error message

Codex review timed out

What it means

After spawning the Codex CLI via spawnSync, the library inspects result.error. Node reports child-process timeouts with error.code === 'ETIMEDOUT', which occurs when the spawned process exceeds the timeout option. The library maps that to a friendly 'Codex review timed out' error instead of leaking a raw SystemError.

Solutions

  1. Increase timeoutMs (up to the 120000 ms cap) and retry the review.
  2. Reduce the prompt size so the review completes faster.
  3. Verify the Codex CLI responds at all: run `codex --version` manually; a hung/stale installation should be reinstalled.
  4. Catch the error and fall back to another review provider in the council setup.
  5. Check machine load/CI concurrency; Codex may simply be starved of CPU.

Example fix

// before
await runReview({ prompt: hugePacket, consent: true, timeoutMs: 10_000 });
// after
await runReview({ prompt: hugePacket.sliceSections(0, 3), consent: true, timeoutMs: 120_000 });
Defensive patterns

Strategy: retry

Validate before calling

const estimateMs = (bytes) => Math.min(Math.max(bytes / 1024, 10) * 1000, 120_000);
const timeoutMs = Math.max(opts.timeoutMs ?? 0, estimateMs(Buffer.byteLength(prompt)));
if (timeoutMs > 120_000) splitPrompt(prompt); // reduce work instead of raising timeout

Try / catch

try {
  return await runReview({ ...opts, timeoutMs: 120_000 });
} catch (e) {
  if (e.message === 'Codex review timed out') return fallbackProvider(opts);
  throw e;
}

Prevention

When it happens

Trigger: runReview spawns the Codex CLI with a valid timeoutMs, the Codex process runs longer than options.timeoutMs, and Node kills it, returning result.error with code ETIMEDOUT.

Common situations: Very large review packets (up to MAX_PROMPT_BYTES) given a minimal 10s timeout; slow machine or cold Codex CLI startup; network-bound Codex operations with a tight 120s cap; CI runners with degraded performance.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/48c9b5b0b9a4162e. Report an issue: GitHub.

Appendix: source

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

  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',
      timeout: options.timeoutMs,
      maxBuffer: 1024 * 1024,
      windowsHide: true,
    });

    if (result.error) {
      if (result.error.code === 'ETIMEDOUT') throw new Error('Codex review timed out');
      if (result.error.code === 'ENOENT') throw new Error('Codex CLI is not installed');
      throw new Error(`Codex invocation failed: ${result.error.message}`);
    }
    if (result.status !== 0) {
      const detail = (result.stderr || '').trim().split('\n').slice(-1)[0];
      throw new Error(`Codex review failed${detail ? `: ${detail}` : ''}`);
    }

    let text;
    try {
      text = readFile(outputFile, 'utf8').trim();
    } catch (error) {
      throw new Error(`Codex returned no final response: ${error.message}`);
    }
    if (!text) throw new Error('Codex returned an empty final response');
    return `${providerLabel(options.hostProvider)}\n${text}`;
  } finally {
    remove(tempDir, { recursive: true, force: true });

View on GitHub (pinned to 8321021c54)