can1357/oh-my-pi · error · Error
--resume: ${jobDir} has no harbor config.json (not a harbor
Error message
--resume: ${jobDir} has no harbor config.json (not a harbor job dir) What it means
resolveResumeConfig reconstructs a Config from a previous job directory. It first reads <jobDir>/config.json to verify the directory actually is a harbor job dir; if the file is missing or unparseable (readJson returns null), it throws rather than guessing, because all subsequent reconstruction depends on that config.
Source
Thrown at packages/metaharness/src/runner.ts:397
dataset?: string;
config?: LaunchRequest;
}
/**
* Recover the original launch Config for `--resume <job>` — nothing needs
* re-specifying. Prefers the exact Config snapshot recorded at launch
* (`_bench/<job>/runner-config.json`), falling back to rebuilding runner argv
* from the manager.json launch record of API-launched runs. The job dir's own
* harbor config.json decides the container backend: harbor rejects a resume
* whose reconstructed config differs from the recorded one.
*/
export function resolveResumeConfig(cli: Config): Config {
const spec = cli.resume as string;
const jobsDir = spec.includes(path.sep) ? path.dirname(path.resolve(spec)) : cli.jobsDir;
const jobName = path.basename(spec);
const jobDir = path.join(jobsDir, jobName);
const jobConfig = readJson(path.join(jobDir, "config.json")) as { environment?: { type?: string } } | null;
if (!jobConfig) throw new Error(`--resume: ${jobDir} has no harbor config.json (not a harbor job dir)`);
let cfg: Config | null = null;
const saved = readJson(path.join(jobsDir, "_bench", jobName, "runner-config.json"));
if (saved && typeof saved === "object") {
cfg = { ...defaultConfig(), ...(saved as Partial<Config>) };
} else {
const manager = readJson(path.join(jobDir, "manager.json")) as ManagerRecord | null;
if (manager?.config) {
if (manager.benchmark && manager.benchmark !== "harbor") {
throw new Error(`--resume supports only harbor runs (${jobName} is ${manager.benchmark})`);
}
const dataset = manager.config.dataset ?? manager.dataset ?? "terminal-bench@2.0";
cfg = parseArgs(harborRunnerArgs(manager.config, { jobsDir, jobName, dataset }));
}
}
if (!cfg) {
throw new Error(
`--resume: no recorded launch config for ${jobName} ` +View on GitHub (pinned to 9690622007)
Solutions
- Verify the directory exists and contains config.json: `ls <jobsDir>/<jobName>/config.json`.
- Use --resume with just the job name (resolved under --jobs-dir) or a full path to the job dir — mixed forms resolve differently (path separator triggers dirname resolution).
- Recreate the job if config.json was deleted; resume cannot proceed without it.
- Point --jobs-dir at the directory that actually contains your job dirs.
Example fix
// before runner --resume myjob # jobs dir default, job actually in /tmp/runs/myjob // after runner --resume /tmp/runs/myjob # or: runner --jobs-dir /tmp/runs --resume myjob
Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs";
const jobDir = spec.includes(path.sep) ? path.dirname(path.resolve(spec)) : path.join(jobsDir, spec);
if (!fs.existsSync(path.join(jobDir, "config.json"))) {
throw new Error(`not a harbor job dir (no config.json): ${jobDir}`);
} Type guard
function isJobDir(p: string): boolean {
return fs.existsSync(path.join(p, "config.json"));
} Try / catch
try {
await resumeJob(spec);
} catch (e) {
if (e instanceof Error && e.message.includes("has no harbor config.json")) {
console.error(`No harbor job at '${spec}'. Check job name and --jobs-dir.`);
} else throw e;
} Prevention
- List the jobs dir and confirm the job name exists before resuming.
- Don't clean config.json out of job dirs; keep job dirs intact for resume.
- Use a consistent --jobs-dir across invocations so name-based lookup resolves where you expect.
When it happens
Trigger: `runner --resume <name>` where <jobsDir>/<name>/config.json does not exist; --resume given a path whose parent dir is the wrong folder; the job dir was partially deleted or cleanup removed config.json; typo'd job name so the dir itself doesn't exist.
Common situations: Resuming after manually clearing the job directory; passing a job from a different benchmark tool that has no harbor config.json; running from the wrong cwd so cli.jobsDir points elsewhere; truncation of the spec path.
Related errors
- ${name} path does not exist: ${trimmed}
- Snippet file not found: ${resolved}
- File not found: ${filePath}
- --resume supports only harbor runs (${jobName} is ${manager.
- --resume: no recorded launch config for ${jobName} (missing
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4fc6a4766ecf0ffc.
Report an issue: GitHub.