can1357/oh-my-pi · error · ToolError
killed ? `Tab ${JSON.stringify(name)} was killed: ${killed}.
Error message
killed ? `Tab ${JSON.stringify(name)} was killed: ${killed}. Reopen it.` : `Tab ${JSON.stringify(name)} is not alive. Open it first with action:"open".` What it means
runInTabWithSnapshot looks the named tab up in the registry; if it is missing or marked dead it throws ToolError telling the caller the tab was killed (with the kill reason) or simply is not alive, and to open it first. This guards against running code in a browser tab that no longer exists.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-supervisor.ts:514
name,
{ code: opts.code, timeoutMs: opts.timeoutMs, signal: opts.signal, session: opts.session },
{
cwd: opts.session.cwd,
browserScreenshotDir: expandBrowserScreenshotDir(opts.session),
excludeWebP: webpExclusionForModel(opts.session.getActiveModel?.()),
},
);
}
async function runInTabWithSnapshot(
name: string,
opts: { code: string; timeoutMs: number; signal?: AbortSignal; session?: ToolSession },
snapshot: SessionSnapshot,
): Promise<RunResultOk> {
const tab = tabs.get(name);
if (!tab || tab.state === "dead") {
const killed = killedTabs.get(name);
throw new ToolError(
killed
? `Tab ${JSON.stringify(name)} was killed: ${killed}. Reopen it.`
: `Tab ${JSON.stringify(name)} is not alive. Open it first with action:"open".`,
);
}
if (tab.pending.size > 0) throw new ToolError(`Tab ${JSON.stringify(name)} is busy`);
const id = Snowflake.next();
const { promise, resolve, reject } = Promise.withResolvers<RunResultOk>();
// `releaseTab` calls `pending.reject(closeError)` when the tab dies
// out from under an in-flight run (sibling `browser close --all`,
// session-scoped reap, etc.). Both backends below MUST end up awaiting
// this same `promise` so:
// 1. The caller sees `Tab ... was closed` immediately instead of
// blocking to the run's timeout, and
// 2. `reject(...)` always has an attached handler — a zero-consumer
// rejection would fire `unhandledRejection` and the CLI's
// top-level handler would tear the whole session down, killing
// every other tab and subagent sharing the process (issue #4499).View on GitHub (pinned to 9690622007)
Solutions
- Re-open the tab with action:"open" (same name) and retry the operation.
- If it was killed, read the kill reason in the message and address it (e.g. avoid the triggering action).
- Check liveness before running: try getTab/inspect the tab list, or wrap the run in try-catch and reopen on this error.
- Avoid the crash path: keep the browser process alive and do not issue close --all while tabs are still needed.
Example fix
// before
await browserTool({ action: "eval", name: "docs", code }); // throws if dead
// after
try {
await browserTool({ action: "eval", name: "docs", code });
} catch (e) {
if (/not alive|was killed/.test(e.message)) {
await browserTool({ action: "open", name: "docs", url });
await browserTool({ action: "eval", name: "docs", code });
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const tab = getTab(name);
if (!tab || tab.state === "dead") {
await browserTool({ action: "open", name, url });
} Type guard
function isRunnableTab(t: ReturnType<typeof getTab>): boolean {
return !!t && t.state !== "dead";
} Try / catch
try {
await runInTab(name, code);
} catch (e) {
if (e instanceof ToolError && /was killed|not alive/.test(e.message)) {
await browserTool({ action: "open", name, url });
await runInTab(name, code);
} else throw e;
} Prevention
- Reopen tabs at the start of each turn if session cleanup may have closed them
- Track kill reasons and avoid the actions that triggered kills
- Don't issue 'browser close --all' while tabs are still needed
- Check tab liveness before long-running scripts
When it happens
Trigger: Calling eval/navigate-style actions on a tab name that was never opened, was closed, or whose browser crashed (state 'dead'); killedTabs may carry the reason of a previous kill.
Common situations: Tab killed by 'browser close --all' or a session reap while the agent still references it; browser process crashed and all tabs went dead; typo in tab name; script assumes a tab persists across turns but session-scoped cleanup closed it.
Related errors
- directory stack is empty
- No messages to continue from
- Cannot continue from message role: assistant
- Cursor blob not found
- Not a git repository
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4c5688c232cbf810.
Report an issue: GitHub.