Hmbown/CodeWhale · error · ExecError
docker exec : timed out
Error message
docker exec ${container}: timed out What it means
The Docker transport runs the remote agent via `docker exec` with a timeout (default 25s). When the run exceeds that limit it throws this ExecError signalling the docker exec timed out. It exists so callers can distinguish slow-hanging container commands from hard failures.
Solutions
- Raise the timeout: pass { timeoutMs: 60000 } (or higher) in opts to remote()
- Check container health: docker ps, docker logs <container>, docker stats
- Retry the request — a transient stall often resolves
- Ensure the container is running and not paused: docker unpause <container>
- Investigate the agent-side operation (e.g. screenshot) that may block indefinitely
Example fix
// before
await ex.remote({ tool: "screenshot" }); // default 25s too short
// after
await ex.remote({ tool: "screenshot" }, { timeoutMs: 120_000 }); Defensive patterns
Strategy: retry
Validate before calling
// check container is running and responsive before the call
const st = await run("docker", ["inspect", "-f", "{{.State.Running}}", container]);
if (st.code !== 0 || st.stdout.trim() !== "true") throw new Error(`container ${container} not running`); Type guard
function isDockerTimeout(e) { return e instanceof ExecError && e.message.includes(": timed out"); } Try / catch
try {
return await ex.remote(request, opts);
} catch (e) {
if (e.message.includes("timed out") && attempts < 3) return retryWithBackoff(attempts + 1, { ...opts, timeoutMs: (opts.timeoutMs ?? 25000) * 2 });
throw e;
} Prevention
- Pass an explicit timeoutMs suited to the tool (screenshots need more than 25s)
- Monitor container health (docker stats) for slow hosts
- Avoid calling remote tools while the container is paused or overloaded
When it happens
Trigger: executor.remote() on a docker transport where the `docker exec` invocation does not complete within opts.timeoutMs (default 25_000 ms): container paused, overloaded host, agent blocking on I/O, or docker daemon unresponsive.
Common situations: Container under heavy load or OOM-swapping; docker daemon stalled; agent waiting on a GUI/screenshot operation that never returns; host disk contention; running on a very slow remote machine.
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
- docker exec : agent returned no JSON receipt
- docker exec exited
- spawn_failed
- child wall-time budget exhausted; continuation cannot reset…
- child wall-time budget exhausted
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/5b7ddb7154fe77f9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/transport.mjs:258
* the container entrypoint recorded before serving requests.
*/
export function dockerExec(computer, binding) {
const container = safeRemotePath(computer.container);
const remoteAgent = "/app/docker/agent-exec.sh";
const ex = {
kind: "docker",
container,
remoteAgent,
run(cmd, args = [], opts = {}) {
// Local side commands (docker itself) run directly.
return run(cmd, args, opts);
},
async remote(request, opts = {}) {
const r = await run("docker", ["exec", container, "/bin/sh", remoteAgent, b64({ args: request.args ?? {}, tool: request.tool, nonce: crypto.randomBytes(6).toString("hex") })], {
timeoutMs: opts.timeoutMs ?? 25_000,
});
if (r.aborted) throw Object.assign(new ExecError("computer request cancelled", r), { code: "cancelled" });
if (r.timedOut) throw new ExecError(`docker exec ${container}: timed out`, r);
if (r.code !== 0) throw new ExecError(`docker exec ${container} exited ${r.code}: ${r.stderr.trim().slice(0, 400)}`, r);
const line = r.stdout.trim().split("\n").filter((l) => l.startsWith("{")).pop();
const reply = line ? JSON.parse(line) : null;
if (!reply) throw new ExecError(`docker exec ${container}: agent returned no JSON receipt`, r);
return reply;
},
};
attachPersistentChannel(ex, binding, ["docker", "exec", "-i", container, "/bin/sh", remoteAgent, "--serve"]);
return ex;
}
/** Push the self-contained remote agent + src tree to an ssh computer. */
export async function installRemoteAgent(computer) {
const ex = sshExec(computer);
const srcDir = path.join(PLUGIN_ROOT, "src");
const rels = ["agent.mjs"];
for (const dir of ["", "backends"]) {
const full = path.join(srcDir, dir);View on GitHub (pinned to 73e0f67d83)