can1357/oh-my-pi · error · ToolError

Browser runtime started without an active run

Error message

Browser runtime started without an active run

What it means

The browser runtime executes user code through a hooks object obtained from #hooksForActiveRun(); if no active run is registered when the runtime starts, this ToolError is thrown. It signals an internal lifecycle bug: the run cell was not activated (or was already torn down) before the runtime body executed.

Source

Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:1345

						),
					);
				} else {
					rejectCancel(abortError);
				}
				// Cancel in-flight tool calls so user code's awaited proxies reject promptly.
				const toolAbort = timeoutSignal.aborted
					? postmortem.markExpectedCleanupError(new ToolAbortError(undefined, { cause: timeoutSignal.reason }))
					: abortError;
				for (const pending of active.pendingTools.values()) {
					pending.reject(toolAbort);
				}
				active.pendingTools.clear();
			};
			if (signal.aborted) onCancel();
			else signal.addEventListener("abort", onCancel, { once: true });
			try {
				const hooks = this.#hooksForActiveRun();
				if (!hooks) throw new ToolError("Browser runtime started without an active run");
				returnValue = await withBrowserPromiseCombinatorTracking(
					active.rejectionOwner,
					onFloatingRejection,
					async () =>
						await Promise.race([
							runtime.run(msg.code, `browser-run-${msg.id}.js`, hooks, {
								runId: msg.id,
								cwd: msg.session.cwd,
							}),
							cancelRejection,
							floatingFailure.promise,
						]),
				);
				completed = true;
			} finally {
				signal.removeEventListener("abort", onCancel);
			}
		} catch (error) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the browser-run operation — if transient, a fresh dispatch establishes a new active run
  2. Check whether the run was aborted concurrently (abort signal/timing) and serialize start against teardown
  3. Ensure each runtime execution is initiated through the normal tool-call path that registers the active run
  4. If reproducible, report as a worker lifecycle bug with the sequence of operations
Defensive patterns

Strategy: retry

Validate before calling

// ensure a run is active before dispatching
if (worker.activeRun == null) throw new Error('no active browser run — start a run cell first');

Type guard

function hasActiveRun(w: { activeRun: unknown }): boolean {
  return w.activeRun != null;
}

Try / catch

try {
  returnValue = await runtime.run(code, file, hooks, opts);
} catch (err) {
  if (err instanceof ToolError && err.message.includes('without an active run')) {
    await retryWithBackoff(() => startRunAndExecute(code)); // re-register active run then retry
  } else throw err;
}

Prevention

When it happens

Trigger: Runtime started while the worker had no active run — the run scope was cancelled/torn down before execution, a race between abort and run start, or an internal wiring bug in the worker's run lifecycle.

Common situations: Abort signal fired concurrently with run start; nested/reentrant run dispatch; retrying after a cell failure without re-establishing the active run context; version-specific worker lifecycle regressions.

Related errors


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