can1357/oh-my-pi · error · ToolError
Computer session is closed
Error message
Computer session is closed
What it means
ComputerSupervisor.run() rejects any execution request once the supervisor has been closed (this.#closed). After close(), the worker is torn down and no further code can be executed in the computer session; reusing the closed supervisor throws this ToolError instead of hanging.
Source
Thrown at packages/coding-agent/src/tools/computer/supervisor.ts:176
},
) {
this.#session = session;
this.#createWorker = createWorker;
this.#timeouts = timeouts;
this.#callSessionTool = callSessionTool;
}
async capabilities(): Promise<DesktopCapabilities | undefined> {
return this.#latestCapabilities;
}
async run(
code: string,
timeoutMs: number,
snapshot: ComputerSessionSnapshot,
signal?: AbortSignal,
): Promise<ComputerRunOk> {
if (this.#closed) throw new ToolError("Computer session is closed");
if (signal?.aborted) throw new ToolAbortError();
await this.#start();
if (signal?.aborted) throw new ToolAbortError();
const id = `computer-${++this.#nextId}`;
const { promise, resolve, reject } = Promise.withResolvers<ComputerRunOk>();
const pending: PendingRun = { resolve, reject, signal, toolCalls: new Map() };
this.#pending.set(id, pending);
const abort = (): void => {
this.#safeSend({ type: "abort", id });
for (const controller of pending.toolCalls.values()) controller.abort(signal?.reason);
};
if (signal?.aborted) abort();
else signal?.addEventListener("abort", abort, { once: true });
try {
this.#worker?.send({ type: "run", id, code, timeoutMs, session: snapshot });
return await this.#raceWithGrace(promise, timeoutMs);View on GitHub (pinned to 9690622007)
Solutions
- Create a new computer session (new supervisor) for post-close work.
- Guard call sites: check an isClosed/closed flag or lifecycle state before calling run().
- Cancel or drain pending tasks during shutdown so no run() is invoked after close().
Example fix
// before await supervisor.close(); await supervisor.run(code, timeout, snapshot); // throws // after await supervisor.close(); const fresh = new ComputerSupervisor(session); await fresh.run(code, timeout, snapshot);
Defensive patterns
Strategy: validation
Validate before calling
// track close state in your wrapper
if (supervisorClosed) {
supervisor = new ComputerSupervisor(session); // recreate before running
} Try / catch
try {
await supervisor.run(code, timeout, snapshot, signal);
} catch (err) {
if (err instanceof ToolError && err.message === "Computer session is closed") {
supervisor = new ComputerSupervisor(session); // recreate and retry once
return await supervisor.run(code, timeout, snapshot, signal);
} else throw err;
} Prevention
- Route all run() calls through a lifecycle-aware wrapper that recreates closed sessions.
- Drain/cancel in-flight tasks before calling close().
- Avoid caching supervisor instances beyond the tool-call lifetime.
When it happens
Trigger: Calling run() on a ComputerSupervisor after close()/dispose was called, or after the computer tool finished and closed the session, then issuing another run.
Common situations: A queued or retried run lands after session teardown; an agent issues a follow-up computer command after the tool reported completion; a long-lived supervisor reference is reused across requests after shutdown.
Related errors
- Computer session tool bridge is unavailable
- Assertion failed
- DAP adapter ${this.adapter.name} is not running
- Debug session ${root.id} is still active. Terminate it befor
- ${error.message} (rethrown as ToolError from StructuredSubag
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7d336f28b29524ec.
Report an issue: GitHub.