Hmbown/CodeWhale · error · ExecError
timeout after ms
Error message
timeout after ${opts.timeoutMs ?? 20_000}ms: ${cmd} What it means
runOk wraps run() and converts timeouts into this typed ExecError, embedding the timeout duration and the command. The child process did not exit within opts.timeoutMs (default 20000ms), so run() killed it and flagged timedOut. This is a wall-clock deadline failure, not a non-zero exit code.
Solutions
- Increase opts.timeoutMs for the specific slow command (e.g. pass { timeoutMs: 60_000 } for UIA-heavy actions).
- Check the target machine/app health — an unresponsive or modal-blocked app will hang any automation call.
- Reduce work per call: split large scripts, or avoid operations known to block (modal dialogs, network paths).
- Add retry with a longer budget if the slowness is transient (system under load).
Example fix
// before
await runOk('powershell', ['-NoProfile', '-Command', bigScript]);
// after
await runOk('powershell', ['-NoProfile', '-Command', bigScript], { timeoutMs: 60_000 }); Defensive patterns
Strategy: retry
Type guard
const isTimeoutError = (e) => e instanceof Error && /^timeout after \d+ms:/.test(e.message);
Try / catch
try {
return await runOk(cmd, args, { timeoutMs: budgetMs });
} catch (e) {
if (isTimeoutError(e) && attempt < 2) return runWithRetry(cmd, args, budgetMs * 2, attempt + 1);
throw e;
} Prevention
- Budget timeouts per command class (UIA-heavy actions need far more than 20s).
- Detect and handle modal dialogs or hung apps that block automation indefinitely.
- Avoid file operations on slow network paths inside timed scripts.
- Add an exponential-backoff retry wrapper around runOk for known-slow commands.
When it happens
Trigger: Any runOk call whose command exceeds opts.timeoutMs: PowerShell UIA scripts on slow/loaded machines, hung target applications blocking an automation query, a stuck screenshot or input injection, or too small an explicit timeoutMs for a legitimately slow command.
Common situations: Under-provisioned CI machines where 20s PowerShell startup plus automation exceeds the budget; an unresponsive app whose UIA provider blocks; network-mapped paths stalling file operations; callers passing timeoutMs tuned for fast commands to slow ones.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- powershell timed out after
- exited
- Command timed out after
- hook helper did not finish within its timeout
- powershell.exe exited
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/b444283029feec1e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/exec.mjs:156
signal?.addEventListener("abort", abort, { once: true });
});
child.stdin.write(JSON.stringify(message) + "\n");
return await Promise.race([next, cancelled, new Promise((_, reject) => { commandTimer = setTimeout(() => reject(new ExecError("Native input owner did not acknowledge pointer motion")), opts.timeoutMs ?? 20_000); })]);
}
catch (error) { await release().catch(() => {}); throw error; }
finally { clearTimeout(commandTimer); signal?.removeEventListener("abort", abort); }
} };
} catch (error) {
await release().catch(() => {});
throw error;
} finally { clearTimeout(timer); }
}
/** run() and throw a typed error on non-zero exit / timeout. */
export async function runOk(cmd, args = [], opts = {}) {
const r = await run(cmd, args, opts);
if (r.aborted) throw Object.assign(new ExecError("computer request cancelled", r), { code: "cancelled" });
if (r.timedOut) throw new ExecError(`timeout after ${opts.timeoutMs ?? 20_000}ms: ${cmd}`, r);
if (r.code !== 0) throw new ExecError(`${cmd} exited ${r.code}: ${trim(r.stderr || r.stdout)}`, r);
return r;
}
export class ExecError extends Error {
constructor(message, result) {
super(message);
this.name = "ExecError";
this.result = result;
}
}
/** True when the executable exists on PATH (or opts.fullPath exists). */
export async function have(cmd) {
const probe = process.platform === "win32" ? "where" : "which";
const r = await run(probe, [cmd], { timeoutMs: 5000 });
return r.code === 0 && r.stdout.trim().length > 0;
}View on GitHub (pinned to 73e0f67d83)