can1357/oh-my-pi · warning · AggregateError
Failed to dispose one or more eval kernels
Error message
Failed to dispose one or more eval kernels
What it means
During EvalRunner disposal, disposeKernels() tears down Python, Ruby, Julia kernel sessions and VM contexts in parallel via Promise.allSettled. If any teardown promise rejects, all failure reasons are collected into an AggregateError with this message, so one bad kernel does not hide the others' failures.
Source
Thrown at packages/coding-agent/src/session/eval-runner.ts:189
beginDispose(): void {
this.#disposing = true;
}
/** Waits for active work and disposes every retained eval kernel owned by the session. */
async disposeKernels(): Promise<void> {
const settled = await this.#prepareExecutionsForDispose();
if (!settled) {
logger.warn("Detaching retained eval-kernel ownership during dispose while eval execution is still active");
}
const results = await Promise.allSettled([
disposeKernelSessionsByOwner(this.#kernelOwnerId),
disposeRubyKernelSessionsByOwner(this.#kernelOwnerId),
disposeJuliaKernelSessionsByOwner(this.#kernelOwnerId),
disposeVmContextsByOwner(this.#kernelOwnerId),
]);
const errors: unknown[] = [];
for (const result of results) if (result.status === "rejected") errors.push(result.reason);
if (errors.length > 0) throw new AggregateError(errors, "Failed to dispose one or more eval kernels");
}
async #waitForExecutionsToSettle(timeoutMs: number): Promise<boolean> {
const deadline = Date.now() + timeoutMs;
while (this.#activeExecutions.size > 0) {
const remainingMs = deadline - Date.now();
if (remainingMs <= 0) return false;
const settled = await Promise.race([
Promise.allSettled(Array.from(this.#activeExecutions)).then(() => true),
Bun.sleep(remainingMs).then(() => false),
]);
if (!settled && this.#activeExecutions.size > 0) return false;
}
return true;
}
async #prepareExecutionsForDispose(): Promise<boolean> {
if (!(await this.#waitForExecutionsToSettle(3_000))) {View on GitHub (pinned to 9690622007)
Solutions
- Iterate error.errors on the AggregateError to see which kernel(s) failed and why
- Retry dispose() once — stale kernels are often already gone and a second pass succeeds
- Kill leftover kernel processes by the owner id manually if cleanup IPC is stuck
- Log and continue: this happens during teardown, so partial failure usually means resources were already being reclaimed
Example fix
// before
try { await evalRunner.dispose(); } catch (e) { throw e; }
// after
try { await evalRunner.dispose(); } catch (e) {
if (e instanceof AggregateError) {
for (const inner of e.errors) logger.warn('kernel dispose failed', { inner: String(inner) });
} // teardown errors are usually non-fatal
} Defensive patterns
Strategy: try-catch
Type guard
function isKernelDisposeFailure(err: unknown): err is AggregateError {
return err instanceof AggregateError && err.message === 'Failed to dispose one or more eval kernels';
} Try / catch
try {
await evalRunner.dispose();
} catch (err) {
if (err instanceof AggregateError && err.message.includes('eval kernels')) {
for (const inner of err.errors) logger.warn('kernel dispose failed', { inner: String(inner) });
return; // teardown best-effort; do not mask shutdown
}
throw err;
} Prevention
- Treat disposal errors as best-effort — log each error.errors entry and continue shutdown
- Retry dispose once if kernels crashed previously
- Monitor kernel health so crashed kernels are reaped before teardown
- Run disposal with a timeout so a stuck kernel cannot hang shutdown
When it happens
Trigger: Calling dispose()/disposeKernels() when at least one of disposePythonKernelSessionsByOwner, disposeRubyKernelSessionsByOwner, disposeJuliaKernelSessionsByOwner, or disposeVmContextsByOwner rejects — e.g. the kernel process already died and its cleanup IPC fails.
Common situations: Kernel process crashed earlier (OOM, killed by OS) leaving stale registration that cleanup cannot remove; shutdown timeout racing kernel shutdown; platform quirk in one language's kernel teardown; repeated disposal attempts.
Related errors
- Command aborted
- Command timed out
- Python kernel unavailable
- Python execution is unavailable while session disposal is in
- ${this.#options.languageName} kernel is not running
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/672e13623fe5bd5a.
Report an issue: GitHub.