{"record":{"id":"7d336f28b29524ec","repo":"can1357/oh-my-pi","slug":"computer-session-is-closed","errorCode":null,"errorMessage":"Computer session is closed","messagePattern":"Computer session is closed","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/computer/supervisor.ts","lineNumber":176,"sourceCode":"\t\t},\n\t) {\n\t\tthis.#session = session;\n\t\tthis.#createWorker = createWorker;\n\t\tthis.#timeouts = timeouts;\n\t\tthis.#callSessionTool = callSessionTool;\n\t}\n\n\tasync capabilities(): Promise<DesktopCapabilities | undefined> {\n\t\treturn this.#latestCapabilities;\n\t}\n\n\tasync run(\n\t\tcode: string,\n\t\ttimeoutMs: number,\n\t\tsnapshot: ComputerSessionSnapshot,\n\t\tsignal?: AbortSignal,\n\t): Promise<ComputerRunOk> {\n\t\tif (this.#closed) throw new ToolError(\"Computer session is closed\");\n\t\tif (signal?.aborted) throw new ToolAbortError();\n\t\tawait this.#start();\n\t\tif (signal?.aborted) throw new ToolAbortError();\n\n\t\tconst id = `computer-${++this.#nextId}`;\n\t\tconst { promise, resolve, reject } = Promise.withResolvers<ComputerRunOk>();\n\t\tconst pending: PendingRun = { resolve, reject, signal, toolCalls: new Map() };\n\t\tthis.#pending.set(id, pending);\n\t\tconst abort = (): void => {\n\t\t\tthis.#safeSend({ type: \"abort\", id });\n\t\t\tfor (const controller of pending.toolCalls.values()) controller.abort(signal?.reason);\n\t\t};\n\t\tif (signal?.aborted) abort();\n\t\telse signal?.addEventListener(\"abort\", abort, { once: true });\n\n\t\ttry {\n\t\t\tthis.#worker?.send({ type: \"run\", id, code, timeoutMs, session: snapshot });\n\t\t\treturn await this.#raceWithGrace(promise, timeoutMs);","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/computer/supervisor.ts#L158-L194","documentation":"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.","triggerScenarios":"Calling run() on a ComputerSupervisor after close()/dispose was called, or after the computer tool finished and closed the session, then issuing another run.","commonSituations":"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.","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()."],"exampleFix":"// before\nawait supervisor.close();\nawait supervisor.run(code, timeout, snapshot); // throws\n\n// after\nawait supervisor.close();\nconst fresh = new ComputerSupervisor(session);\nawait fresh.run(code, timeout, snapshot);","handlingStrategy":"validation","validationCode":"// track close state in your wrapper\nif (supervisorClosed) {\n  supervisor = new ComputerSupervisor(session); // recreate before running\n}","typeGuard":null,"tryCatchPattern":"try {\n  await supervisor.run(code, timeout, snapshot, signal);\n} catch (err) {\n  if (err instanceof ToolError && err.message === \"Computer session is closed\") {\n    supervisor = new ComputerSupervisor(session); // recreate and retry once\n    return await supervisor.run(code, timeout, snapshot, signal);\n  } else throw err;\n}","preventionTips":["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."],"tags":["lifecycle","use-after-close","computer-tool","tool-error"],"backgroundTag":"use-after-close","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}