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
- Retry the browser-run operation — if transient, a fresh dispatch establishes a new active run
- Check whether the run was aborted concurrently (abort signal/timing) and serialize start against teardown
- Ensure each runtime execution is initiated through the normal tool-call path that registers the active run
- 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
- Always start runtime execution through the normal tool-call path that registers the active run
- Serialize run start against abort/teardown to avoid lifecycle races
- Re-establish run state after any cell failure instead of reusing stale context
- Treat persistent occurrences as a worker bug and report with repro steps
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
- Cannot set cwd on a disposed JS runtime
- Server "${name}" was disconnected during initial connection
- Server "${name}" was disconnected during reconnection
- Python execution is unavailable while session disposal is in
- Recipient session is disposed.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/39cdf7c2ec3b63d9.
Report an issue: GitHub.