can1357/oh-my-pi · error · Error

run ${jobName} is running; cancel it first

Error message

run ${jobName} is running; cancel it first

What it means

deleteRun removes a run's DB rows and on-disk artifacts (job dir and manager log), but refuses while the run is live: if `#runLive(run)` is true it throws telling you to cancel the run first. Unknown job names return false instead of throwing.

Source

Thrown at packages/metaharness/src/server.ts:576

				`experiment ${id} has running arms (${live.map(r => r.jobName).join(", ")}); cancel them first`,
			);
		}
		for (const run of runs) this.#destroyRun(run.jobName);
		this.#store.deleteExperimentMeta(id);
		this.#tick();
		return { id, deletedRuns: runs.map(r => r.jobName) };
	}

	/**
	 * Permanently delete a run: DB row + trials, job dir, and manager log.
	 * Disk removal is not optional — discover() would resurrect a surviving
	 * job dir as a fresh row on the next restart. Refuses while the run is
	 * live; returns false when the run is unknown.
	 */
	deleteRun(jobName: string): boolean {
		const run = this.#store.getRun(jobName);
		if (!run) return false;
		if (this.#runLive(run)) throw new Error(`run ${jobName} is running; cancel it first`);
		this.#destroyRun(jobName);
		this.#tick();
		return true;
	}

	/** Remove a run's DB rows and on-disk artifacts (job dir + manager log). */
	#destroyRun(jobName: string): void {
		assertSafeJobName(jobName);
		this.#store.deleteRun(jobName);
		fs.rmSync(path.join(this.jobsDir, jobName), { recursive: true, force: true });
		fs.rmSync(path.join(this.jobsDir, "_manager", "logs", `${jobName}.log`), { force: true });
	}

	/** Add a comparable arm to an existing experiment, inheriting its sample + config. */
	addArm(experimentId: string, req: AddArmRequest): { jobName: string; pid: number } {
		return this.launch(resolveArmLaunch(this.#store, experimentId, req));
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Cancel the run first (cancel API), then delete it
  2. Wait for the run to finish before deleting
  3. If the process is actually dead but state is stale, restart the manager (which reaps state, e.g. markExit on restart) and retry

Example fix

// before
server.deleteRun("job-7"); // throws: run job-7 is running
// after
if (server.isRunLive?.("job-7")) server.cancelRun("job-7");
server.deleteRun("job-7");
Defensive patterns

Strategy: validation

Validate before calling

function canDeleteRun(server: ManagerServer, jobName: string): boolean {
  const run = server.getRun(jobName);
  return !!run && !server.isRunRunning(jobName);
}

Try / catch

try {
  server.deleteRun(jobName);
} catch (err) {
  if (err instanceof Error && /is running; cancel it first/.test(err.message)) {
    server.cancelRun(jobName);
    server.deleteRun(jobName);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `deleteRun(jobName)` while the run's process is still executing (live per `#runLive`).

Common situations: A cleanup routine deleting jobs without checking status; attempting to purge a run that is mid-trial; operator deletes the wrong still-active job.

Related errors


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