can1357/oh-my-pi · error · ToolError

no active computer run

Error message

no active computer run

What it means

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.

Source

Thrown at packages/coding-agent/src/tools/computer/worker.ts:635

	async #callTool(active: ActiveRun, name: string, args: unknown): Promise<unknown> {
		const id = `computer-tc-${active.id}-${crypto.randomUUID()}`;
		const { promise, resolve, reject } = Promise.withResolvers<unknown>();
		active.pendingTools.set(id, { resolve, reject });
		this.#transport.send({ type: "tool-call", id, runId: active.id, name, args });
		return await promise;
	}

	#deliverToolReply(id: string, reply: ToolReply): void {
		const pending = this.#active?.pendingTools.get(id);
		if (!pending) return;
		this.#active?.pendingTools.delete(id);
		if (reply.ok) pending.resolve(reply.value);
		else pending.reject(replyError(reply.error));
	}

	#currentRunContext = (): ComputerRunContext => {
		const context = this.#runContexts.getStore();
		if (!context) throw new ToolError("no active computer run");
		return context;
	};

	#createDesktopScope(session: NativeDesktopSession): object {
		const getContext = this.#currentRunContext;
		const makeWin = (window: DesktopWindow): Win => new Win(session, getContext, window);
		const el = (node: AxNode): El => new El(session, getContext, node);
		const desktopTarget = new Win(session, getContext, {
			id: "desktop",
			app: "desktop",
			title: "desktop",
			x: 0,
			y: 0,
			width: 0,
			height: 0,
			focused: false,
		});
		return {

View on GitHub (pinned to 9690622007)

Solutions

  1. Perform all desktop interactions synchronously within the computer-run callback scope.
  2. Do not capture/alias the desktop object outside the run; re-run a new computer run for later interactions.
  3. If async work must continue, chain it inside the run before returning so the AsyncLocalStorage context is active.

Example fix

// inside computer run code
// before
window.__desktop = desktop;
setTimeout(() => window.__desktop.screenshot(), 1000); // throws

// after
await wait(1000);            // stay inside the run
await desktop.screenshot();  // context still active
Defensive patterns

Strategy: validation

Validate before calling

// don't escape the facade: do all desktop work inside the run callback
// bad: const d = desktop; later d.screenshot()
// good:
await (async () => { await desktop.screenshot(); })(); // stays in run scope

Try / catch

try {
  await escapedFacade.screenshot();
} catch (err) {
  if (err instanceof ToolError && err.message === "no active computer run") {
    // re-dispatch inside a new computer run
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/a1f102d9fb8d5c2f. Report an issue: GitHub.