{"record":{"id":"409056e1f474dde9","repo":"can1357/oh-my-pi","slug":"jobname-has-no-harbor-config-json-to-resume-fro","errorCode":null,"errorMessage":"${jobName} has no harbor config.json to resume from","messagePattern":"(.+?) has no harbor config\\.json to resume from","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/server.ts","lineNumber":456,"sourceCode":"\t * original launch flags from the run's recorded config, so nothing needs\n\t * re-specifying. `filterErrorTypes` overrides the default retry set\n\t * (every exception type recorded in the job's result.json).\n\t */\n\tresume(jobName: string, opts: { filterErrorTypes?: string[] } = {}): { jobName: string; pid: number } {\n\t\tconst run = this.#store.getRun(jobName);\n\t\tif (!run) throw new Error(`run ${jobName} not found`);\n\t\tif (run.benchmark !== \"harbor\")\n\t\t\tthrow new Error(`resume supports only harbor runs (${jobName} is ${run.benchmark})`);\n\t\t// Trust liveness, not the recorded status: a runner killed while a\n\t\t// previous server instance owned it leaves a stale `running` row with a\n\t\t// dead (or null) pid and nobody to fire markExit.\n\t\tif (this.#runLive(run)) {\n\t\t\tthrow new Error(`run ${jobName} is already running`);\n\t\t}\n\t\tif (run.status === \"running\") this.#store.markExit(jobName, null, true);\n\t\tconst jobDir = path.join(this.jobsDir, jobName);\n\t\tif (!fs.existsSync(path.join(jobDir, \"config.json\"))) {\n\t\t\tthrow new Error(`${jobName} has no harbor config.json to resume from`);\n\t\t}\n\t\tconst argv = [\"bun\", \"src/runner.ts\", \"--resume\", jobName, \"--jobs-dir\", this.jobsDir];\n\t\tfor (const t of opts.filterErrorTypes ?? erroredExceptionTypes(jobDir)) argv.push(\"--filter-error-type\", t);\n\t\tlet prewalk: LaunchRequest[\"prewalk\"];\n\t\ttry {\n\t\t\tprewalk = run.prewalk ? (JSON.parse(run.prewalk) as { into?: string }) : undefined;\n\t\t} catch {\n\t\t\tprewalk = undefined;\n\t\t}\n\t\tconst pid = this.#spawnRunner(argv, PKG_DIR, {\n\t\t\tbenchmark: \"harbor\",\n\t\t\tjobName,\n\t\t\tdataset: run.dataset,\n\t\t\tagent: run.agent,\n\t\t\tmodels: run.models ? run.models.split(\",\") : [],\n\t\t\tprewalk,\n\t\t\tconfig: run.config,\n\t\t\trole: run.role,","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/server.ts#L438-L474","documentation":"ManagerServer throws this when you ask it to resume a harbor job whose job directory contains no config.json. Resume works by re-spawning `runner.ts --resume <jobName> --jobs-dir <jobsDir>`, which requires the original harbor config persisted in `<jobsDir>/<jobName>/config.json`; without it the runner has nothing to resume from. The server treats a missing config as a caller/state error rather than silently failing inside the child.","triggerScenarios":"Calling the resume API (e.g. `resumeRun`/`launchRun` with resume semantics, or the corresponding RPC) for a jobName whose `jobsDir/<jobName>/config.json` does not exist — e.g. resuming a job that was never launched, after the job dir was deleted, or with a typo'd job name.","commonSituations":"Manual cleanup deleted parts of the jobs directory; the job was created in a different jobsDir and you pointed the server at the wrong `--jobs-dir`; typo in jobName; attempting to resume a job that only got as far as a DB row before config.json was written.","solutions":["Verify the job directory exists and contains config.json: `ls <jobsDir>/<jobName>/config.json`","If the directory is gone, relaunch the job from scratch instead of resuming","If the jobsDir is wrong, restart the manager with the correct --jobs-dir path","If the job name is mistyped, list available jobs (`ls <jobsDir>`) and use the exact name"],"exampleFix":"// before\nawait server.resumeRun(\"job-42\"); // throws: job-42 has no harbor config.json\n// after\nconst jobDir = path.join(jobsDir, \"job-42\");\nif (!fs.existsSync(path.join(jobDir, \"config.json\"))) {\n  await server.launchRun(\"job-42\"); // fresh launch instead of resume\n} else {\n  await server.resumeRun(\"job-42\");\n}","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nfunction canResume(jobsDir: string, jobName: string): boolean {\n  return fs.existsSync(path.join(jobsDir, jobName, \"config.json\"));\n}","typeGuard":null,"tryCatchPattern":"try {\n  server.resumeRun(jobName);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"has no harbor config.json\")) {\n    server.launchRun(jobName); // fall back to a fresh launch\n  } else throw err;\n}","preventionTips":["Check config.json exists before calling resume","Never manually delete files under jobsDir","Use the exact jobName from the manager's run listing","Point the manager at the same --jobs-dir used for the original launch"],"tags":["filesystem","state","resume"],"backgroundTag":"missing-resume-config","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}