{"record":{"id":"708bf54220ca4479","repo":"can1357/oh-my-pi","slug":"cannot-action-while-another-same-realm-js-runti","errorCode":null,"errorMessage":"Cannot ${action} while another same-realm JS runtime is running","messagePattern":"Cannot (.+?) while another same-realm JS runtime is running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/runtime.ts","lineNumber":528,"sourceCode":"\tif (!wasTop) return;\n\tconst next = stack.entries.at(-1);\n\tif (next) {\n\t\t(globalThis as Record<string, unknown>)[key] = next.value;\n\t\treturn;\n\t}\n\trestoreGlobal(key, stack.base);\n\tGLOBAL_STACKS.delete(key);\n}\n\n// Plain globalThis cannot safely serve two different runtimes at the same instant:\n// helpers dereference reserved globals on every call. Sequential cmux tab revisits\n// re-activate their owner stack; overlapping cross-runtime runs fail explicitly.\nlet activeGlobalRunOwner: symbol | null = null;\nlet activeGlobalRunDepth = 0;\n\nfunction assertCanUseGlobalOwner(owner: symbol, action: string): void {\n\tif (activeGlobalRunOwner === null || activeGlobalRunOwner === owner) return;\n\tthrow new Error(`Cannot ${action} while another same-realm JS runtime is running`);\n}\n\nfunction activateGlobalOwner(owner: symbol, keys: Iterable<string>, action: string): void {\n\tassertCanUseGlobalOwner(owner, action);\n\tfor (const key of keys) {\n\t\tconst stack = GLOBAL_STACKS.get(key);\n\t\tconst index = stack?.entries.findIndex(entry => entry.owner === owner) ?? -1;\n\t\tif (!stack || index === -1) throw new Error(`Cannot ${action} on a disposed JS runtime`);\n\t\tconst entry = stack.entries[index];\n\t\tstack.entries.splice(index, 1);\n\t\tstack.entries.push(entry);\n\t\t(globalThis as Record<string, unknown>)[key] = entry.value;\n\t}\n}\n\nfunction enterGlobalRun(owner: symbol, action: string): () => void {\n\tassertCanUseGlobalOwner(owner, action);\n\tactiveGlobalRunOwner = owner;","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/runtime.ts#L510-L546","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before: two concurrent runs\nconst [a, b] = await Promise.all([runA(), runB()]);\n// after: serialize\nconst a = await runA();\nconst b = await runB();","handlingStrategy":"try-catch","validationCode":"// track an app-level mutex around global eval runs\nlet runChain = Promise.resolve();\nfunction withExclusiveRun<T>(fn: () => Promise<T>): Promise<T> {\n  const next = runChain.then(fn, fn);\n  runChain = next.catch(() => undefined);\n  return next;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await withExclusiveRun(() => runtime.run(code));\n} catch (err) {\n  if (err instanceof Error && /another same-realm JS runtime is running/.test(err.message)) {\n    // queue the run until the active owner finishes\n  }\n}","preventionTips":["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"],"tags":["concurrency","runtime","globals","eval"],"backgroundTag":"concurrent-runtime-owner-conflict","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}