can1357/oh-my-pi · error · Error

Cannot set cwd on a disposed JS runtime

Error message

Cannot set cwd on a disposed JS runtime

What it means

`setCwd` explicitly checks `#disposed` and throws `Cannot set cwd on a disposed JS runtime`. The comment documents why the throw is deliberate but guarded: WorkerCore/browser/cmux call setCwd from init/pre-run paths that can race another same-realm runtime, and an escaping throw used to become a fatal unhandledRejection that killed the whole session. Callers must treat this as a signal to recreate the runtime.

Source

Thrown at packages/coding-agent/src/eval/js/shared/runtime.ts:217

		this.sessionId = opts.sessionId;
		this.#env = new Map();
		this.#moduleLoader = new LocalModuleLoader(this.sessionId);
		this.#localRoots = opts.localRoots ?? {};
		this.helpers = createHelpers({
			cwd: () => this.#activeCwd(),
			env: this.#env,
			localRoots: () => this.#localRoots,
			emitStatus: event => this.#activeHooks("emitStatus")?.onDisplay({ type: "status", event }),
		});
		this.#install(opts.extraGlobals);
	}

	get cwd(): string {
		return this.#cwd;
	}

	setCwd(cwd: string): void {
		if (this.#disposed) throw new Error("Cannot set cwd on a disposed JS runtime");
		// Always stamp the runtime and session state: WorkerCore/browser/cmux call
		// setCwd from init and pre-run paths that may race another same-realm
		// runtime, and a throw here used to escape the inline-worker microtask
		// path as a fatal unhandledRejection that killed the whole session.
		// #session is the same object saved in this owner's global stack entry,
		// so the new cwd survives deferred activation and is visible to this
		// runtime's next run; run()/setRunScope still assert exclusive ownership.
		this.#cwd = cwd;
		this.#session.cwd = cwd;
		if (activeGlobalRunOwner === null || activeGlobalRunOwner === this.#globalOwner) {
			this.#activateGlobals("set cwd");
		}
	}

	/**
	 * Install per-run globals. Intended for run-scoped state (browser's `tab`, `display`
	 * overrides, etc.). Overwrites previous assignments — caller is responsible for any
	 * cleanup it wants.

View on GitHub (pinned to 9690622007)

Solutions

  1. Guard the setCwd call with a disposed/liveness check on the runtime before invoking it.
  2. Wrap init/pre-run cwd updates in try-catch and treat disposal as a no-op (abort the stale path) rather than a fatal error.
  3. Recreate the runtime and re-apply the cwd to the new instance if the cwd change must take effect.

Example fix

// before
runtime.setCwd(newCwd); // may throw fatal unhandledRejection if disposed
// after
try {
	runtime.setCwd(newCwd);
} catch (err) {
	if (!(err instanceof Error && err.message.includes("disposed"))) throw err;
	// stale runtime: drop it; next run creates a fresh one with the current cwd
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (runtimeDisposed) return; // skip stale cwd stamp

Try / catch

try {
	runtime.setCwd(cwd);
} catch (err) {
	if (!(err instanceof Error && err.message === "Cannot set cwd on a disposed JS runtime")) throw err;
	// stale runtime raced teardown: drop it; next run recreates with current cwd
}

Prevention

When it happens

Trigger: Calling `runtime.setCwd(newCwd)` after the runtime was disposed — typically from an inline-worker init or pre-run path racing session teardown, or deferred activation code re-stamping cwd on a dead runtime.

Common situations: Same-realm worker fallback where two runtimes share a global and teardown of one disposes the other's cwd stamp; asynchronous init resolving after the session was killed; session cwd change events delivered post-dispose.

Related errors


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