{"record":{"id":"a1f102d9fb8d5c2f","repo":"can1357/oh-my-pi","slug":"no-active-computer-run","errorCode":null,"errorMessage":"no active computer run","messagePattern":"no active computer run","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/computer/worker.ts","lineNumber":635,"sourceCode":"\tasync #callTool(active: ActiveRun, name: string, args: unknown): Promise<unknown> {\n\t\tconst id = `computer-tc-${active.id}-${crypto.randomUUID()}`;\n\t\tconst { promise, resolve, reject } = Promise.withResolvers<unknown>();\n\t\tactive.pendingTools.set(id, { resolve, reject });\n\t\tthis.#transport.send({ type: \"tool-call\", id, runId: active.id, name, args });\n\t\treturn await promise;\n\t}\n\n\t#deliverToolReply(id: string, reply: ToolReply): void {\n\t\tconst pending = this.#active?.pendingTools.get(id);\n\t\tif (!pending) return;\n\t\tthis.#active?.pendingTools.delete(id);\n\t\tif (reply.ok) pending.resolve(reply.value);\n\t\telse pending.reject(replyError(reply.error));\n\t}\n\n\t#currentRunContext = (): ComputerRunContext => {\n\t\tconst context = this.#runContexts.getStore();\n\t\tif (!context) throw new ToolError(\"no active computer run\");\n\t\treturn context;\n\t};\n\n\t#createDesktopScope(session: NativeDesktopSession): object {\n\t\tconst getContext = this.#currentRunContext;\n\t\tconst makeWin = (window: DesktopWindow): Win => new Win(session, getContext, window);\n\t\tconst el = (node: AxNode): El => new El(session, getContext, node);\n\t\tconst desktopTarget = new Win(session, getContext, {\n\t\t\tid: \"desktop\",\n\t\t\tapp: \"desktop\",\n\t\t\ttitle: \"desktop\",\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\twidth: 0,\n\t\t\theight: 0,\n\t\t\tfocused: false,\n\t\t});\n\t\treturn {","sourceCodeStart":617,"sourceCodeEnd":653,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/computer/worker.ts#L617-L653","documentation":"Desktop facade objects resolve their run context via AsyncLocalStorage (#runContexts). #currentRunContext() throws 'no active computer run' when a facade method is invoked outside an active run — i.e. the desktop scope was captured/escaped from the run callback and called later, or a bound facade is invoked where no run context is stored.","triggerScenarios":"Calling a desktop facade method (screenshot, win, click, …) after the run finished (callback returned), storing the desktop object in a global/outer variable and using it asynchronously beyond the run, or invoking a bound facade from a worker message handler outside the run scope.","commonSituations":"Agent scripts save `const d = desktop` and call d.screenshot() in a later tick/callback; a promise started inside the run resolves after the AsyncLocalStorage context is gone; facade objects leak into timers or event handlers.","solutions":["Perform all desktop interactions synchronously within the computer-run callback scope.","Do not capture/alias the desktop object outside the run; re-run a new computer run for later interactions.","If async work must continue, chain it inside the run before returning so the AsyncLocalStorage context is active."],"exampleFix":"// inside computer run code\n// before\nwindow.__desktop = desktop;\nsetTimeout(() => window.__desktop.screenshot(), 1000); // throws\n\n// after\nawait wait(1000);            // stay inside the run\nawait desktop.screenshot();  // context still active","handlingStrategy":"validation","validationCode":"// don't escape the facade: do all desktop work inside the run callback\n// bad: const d = desktop; later d.screenshot()\n// good:\nawait (async () => { await desktop.screenshot(); })(); // stays in run scope","typeGuard":null,"tryCatchPattern":"try {\n  await escapedFacade.screenshot();\n} catch (err) {\n  if (err instanceof ToolError && err.message === \"no active computer run\") {\n    // re-dispatch inside a new computer run\n  } else throw err;\n}","preventionTips":["Never store the desktop object in outer variables, globals, or closures that outlive the run callback.","Await all async work inside the run before returning, so AsyncLocalStorage context stays active.","Structure scripts as one run per interaction batch rather than capturing the facade for later use."],"tags":["async-local-storage","scope","computer-tool","run-context"],"backgroundTag":"missing-run-context","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}