can1357/oh-my-pi · error · Error

run ${jobName} is already running

Error message

run ${jobName} is already running

What it means

Before spawning a runner, launch() checks both this.#children (live child processes this server owns) and the run store for a run whose status is 'running'. If either shows activity for the derived or requested jobName, a duplicate launch is refused to prevent two runners writing the same job directory. This error means the job name is already live.

Source

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

			},
		});
	}

	/** Launch any supported benchmark and register it in the uniform run store. */
	launch(request: LaunchRequest): { jobName: string; pid: number } {
		if (!request.model) throw new Error("model is required");
		const benchmark = request.benchmark ?? "harbor";
		if (benchmark !== "harbor" && benchmark !== "edit" && benchmark !== "snapcompact") {
			throw new Error(`unsupported benchmark: ${benchmark}`);
		}
		const dataset =
			request.dataset ??
			(benchmark === "harbor" ? "terminal-bench@2.0" : benchmark === "edit" ? "typescript-edit" : "squad-dev");
		const stamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
		const modelSlug = request.model.replace(/[^a-zA-Z0-9]+/g, "-");
		const jobName = request.jobName ?? `${modelSlug}-${stamp}`;
		if (this.#children.has(jobName) || this.#store.getRun(jobName)?.status === "running") {
			throw new Error(`run ${jobName} is already running`);
		}
		const jobDir = path.join(this.jobsDir, jobName);
		fs.mkdirSync(jobDir, { recursive: true });

		let argv: string[];
		let cwd: string;
		if (benchmark === "edit") {
			cwd = PKG_DIR;
			argv = ["bun", "adapters/edit/cli.ts", "--model", request.model, "--output", path.join(jobDir, "result.json")];
			if (request.tasks !== undefined) argv.push("--max-tasks", String(request.tasks));
			if (request.include?.length) argv.push("--tasks", request.include.join(","));
			if (request.concurrency !== undefined) argv.push("--task-concurrency", String(request.concurrency));
			if (request.attempts !== undefined) argv.push("--runs", String(request.attempts));
		} else if (benchmark === "snapcompact") {
			cwd = PKG_DIR;
			argv = ["uv", "run", "src/adapters/snapcompact.py", "--model", request.model, "--output-dir", jobDir];
			if (request.tasks !== undefined) argv.push("--limit-paras", String(request.tasks));
			if (request.concurrency !== undefined) argv.push("--workers", String(request.concurrency));

View on GitHub (pinned to 9690622007)

Solutions

  1. Wait for the existing run to finish, or kill the live runner process before relaunching.
  2. Omit request.jobName or change it — the default name embeds a timestamp, so letting it auto-generate avoids collisions.
  3. Check the store: if the row says 'running' but the pid is dead, mark the exit (the resume path does this via markExit) or restart the server to reconcile.
  4. Add application-level locking in your launcher script to prevent overlapping scheduled launches.

Example fix

// before
server.launch({ model: "m1", jobName: "m1-fixed" }); // called again while running
// after
const run = server.listRuns().find(r => r.jobName === "m1-fixed");
if (!run || run.status !== "running") {
  server.launch({ model: "m1", jobName: "m1-fixed" });
}
Defensive patterns

Strategy: validation

Validate before calling

const jobName = request.jobName ?? defaultName(request);
const live = store.listRuns().some(r => r.jobName === jobName && r.status === "running");
if (live) throw new Error(`${jobName} still running; skipping launch`);

Try / catch

try {
  server.launch(req);
} catch (err) {
  if (err instanceof Error && err.message.includes("is already running")) {
    console.warn(`run ${req.jobName} active; waiting 60s before retry`);
    await Bun.sleep(60_000);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling launch() twice with the same explicit request.jobName while the first is still running; re-running a launch script quickly enough that the previous runner hasn't exited; a stale store row with status 'running' left by a crashed server (no markExit fired); a custom jobName colliding with an existing run.

Common situations: Cron jobs overlapping; CI retry re-invoking launch after a timeout while the first runner is still alive; server restart orphaning 'running' rows; two operators launching the same named experiment concurrently.

Related errors


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