can1357/oh-my-pi · error · Error

Python execution is unavailable while session disposal is in

Error message

Python execution is unavailable while session disposal is in progress

What it means

EvalRunner.assertExecutionAllowed() guards against starting new Python/eval work once the session's disposal sequence has begun. Once #disposing is set, executePython() and execution() throw this synchronously instead of queueing work that could never complete or would race the teardown.

Source

Thrown at packages/coding-agent/src/session/eval-runner.ts:93

				});
			const result = await executePythonCommand(code, {
				cwd,
				sessionId: namespacePythonSessionId(sessionId),
				kernelOwnerId: this.#kernelOwnerId,
				kernelMode: this.#host.settings.get("python.kernelMode"),
				interpreter: this.#host.settings.get("python.interpreter")?.trim() || undefined,
				onChunk,
				signal: abortController.signal,
			});
			this.recordPythonResult(code, result, options);
			return result;
		})();
		return await this.trackExecution(execution, abortController);
	}

	/** Rejects new eval work once session disposal begins. */
	assertExecutionAllowed(): void {
		if (this.#disposing) throw new Error("Python execution is unavailable while session disposal is in progress");
	}

	/** Tracks externally started Python work so disposal can await and abort it. */
	trackExecution<T>(execution: Promise<T>, abortController: AbortController): Promise<T> {
		this.#abortControllers.add(abortController);
		this.#activeExecutions.add(execution);
		void execution.then(
			() => {
				this.#abortControllers.delete(abortController);
				this.#activeExecutions.delete(execution);
			},
			() => {
				this.#abortControllers.delete(abortController);
				this.#activeExecutions.delete(execution);
			},
		);
		return execution;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Check a disposed flag / isDisposed before invoking executePython and skip the work
  2. Await dispose() completion before letting pending tasks run, or cancel pending tasks at shutdown start
  3. Wrap late calls in try-catch and treat this error as an expected shutdown signal, not a bug
  4. Reorder teardown so eval work is drained/aborted before disposal begins

Example fix

// before
await evalRunner.executePython(code); // throws during shutdown
// after
if (session.isDisposed()) return; // or AbortSignal check
try {
  await evalRunner.executePython(code);
} catch (e) {
  if (!(e instanceof Error && e.message.includes('disposal is in progress'))) throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// gate calls on session liveness:
// if (session.isDisposed()) return; // skip eval work on a dying session

Try / catch

try {
  const result = await evalRunner.executePython(code);
} catch (err) {
  if (err instanceof Error && err.message.includes('disposal is in progress')) {
    return null; // expected during shutdown — abort quietly
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling evalRunner.executePython(...) or evalRunner.execution(...) after dispose() has started on the session — e.g. a background task, timer, or streaming response that fires code execution while the app is shutting the session down.

Common situations: App shutdown or session switch while an agent loop is still mid-turn; a queued follow-up tool call executing after the user aborted/disposed; tests tearing down a session while async callbacks still fire.

Related errors


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