can1357/oh-my-pi · error · Error
Cannot ${action} while another same-realm JS runtime is runn
Error message
Cannot ${action} while another same-realm JS runtime is running What it means
The JS eval runtime supports only one active global-owning runtime per JS realm. `assertCanUseGlobalOwner` checks the module-level `activeGlobalRunOwner` symbol and throws when a runtime different from the currently active one attempts to install/activate globals or enter a global run. This prevents two same-realm runtimes (e.g. two concurrent eval sessions sharing the vm context) from silently clobbering each other's injected globals.
Source
Thrown at packages/coding-agent/src/eval/js/shared/runtime.ts:528
if (!wasTop) return;
const next = stack.entries.at(-1);
if (next) {
(globalThis as Record<string, unknown>)[key] = next.value;
return;
}
restoreGlobal(key, stack.base);
GLOBAL_STACKS.delete(key);
}
// Plain globalThis cannot safely serve two different runtimes at the same instant:
// helpers dereference reserved globals on every call. Sequential cmux tab revisits
// re-activate their owner stack; overlapping cross-runtime runs fail explicitly.
let activeGlobalRunOwner: symbol | null = null;
let activeGlobalRunDepth = 0;
function assertCanUseGlobalOwner(owner: symbol, action: string): void {
if (activeGlobalRunOwner === null || activeGlobalRunOwner === owner) return;
throw new Error(`Cannot ${action} while another same-realm JS runtime is running`);
}
function activateGlobalOwner(owner: symbol, keys: Iterable<string>, action: string): void {
assertCanUseGlobalOwner(owner, action);
for (const key of keys) {
const stack = GLOBAL_STACKS.get(key);
const index = stack?.entries.findIndex(entry => entry.owner === owner) ?? -1;
if (!stack || index === -1) throw new Error(`Cannot ${action} on a disposed JS runtime`);
const entry = stack.entries[index];
stack.entries.splice(index, 1);
stack.entries.push(entry);
(globalThis as Record<string, unknown>)[key] = entry.value;
}
}
function enterGlobalRun(owner: symbol, action: string): () => void {
assertCanUseGlobalOwner(owner, action);
activeGlobalRunOwner = owner;View on GitHub (pinned to 9690622007)
Solutions
- Serialize the overlapping runs: await the first global run before starting the second
- Use a separate runtime instance/realm for concurrent work so each has its own owner symbol
- Ensure the first runtime properly exits its run so `activeGlobalRunOwner` returns to null (check for leaked runs on error paths)
Example fix
// before: two concurrent runs const [a, b] = await Promise.all([runA(), runB()]); // after: serialize const a = await runA(); const b = await runB();
Defensive patterns
Strategy: try-catch
Validate before calling
// track an app-level mutex around global eval runs
let runChain = Promise.resolve();
function withExclusiveRun<T>(fn: () => Promise<T>): Promise<T> {
const next = runChain.then(fn, fn);
runChain = next.catch(() => undefined);
return next;
} Try / catch
try {
await withExclusiveRun(() => runtime.run(code));
} catch (err) {
if (err instanceof Error && /another same-realm JS runtime is running/.test(err.message)) {
// queue the run until the active owner finishes
}
} Prevention
- Never start two same-realm JS runtime global runs concurrently — always await the previous run
- Wrap every global run in the same queue/mutex helper
- Ensure error paths exit runs so the owner is released
When it happens
Trigger: Calling `#install`, `activateGlobalOwner`, or `enterGlobalRun` on a second JS runtime while `activeGlobalRunOwner` is set to a different symbol — i.e. two overlapping cross-runtime eval runs in the same process realm.
Common situations: Running two eval/kernel sessions concurrently (e.g. a JS kernel and a second JS runtime instance), or background tasks overlapping with an interactive eval run that both try to install globals on the shared global stack.
Related errors
- native spelling thread stopped
- Agent is already processing. Use steer() or followUp() to qu
- Failed to open auth database at '${dbPath}' after ${maxAttem
- OAuth refresh ownership was lost before persistence
- No credential with id=${id}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/708bf54220ca4479.
Report an issue: GitHub.