can1357/oh-my-pi · error · Error
invalid job name: ${jobName}
Error message
invalid job name: ${jobName} What it means
Job names are used as path segments under the jobs directory, so the server rejects any name that is empty, '.', '..', or contains a path separator (/ or \). This is a path-traversal guard: it throws before a job name is ever used in a filesystem operation such as destroying a run (#destroyRun).
Source
Thrown at packages/metaharness/src/server.ts:96
controller: ReadableStreamDefaultController<Uint8Array>;
state: SseState;
}
function parseServerArgs(argv: string[]): { port: number; jobsDir: string } {
let port = 4700;
let jobsDir = DEFAULT_JOBS_DIR;
for (let i = 0; i < argv.length; i++) {
if (argv[i] === "--port" && argv[i + 1]) port = Number(argv[++i]);
else if (argv[i] === "--jobs-dir" && argv[i + 1]) jobsDir = path.resolve(argv[++i]);
}
if (!Number.isSafeInteger(port) || port < 1 || port > 65535) throw new Error("--port must be 1..65535");
return { port, jobsDir };
}
/** Job names are single path segments; anything else could escape the jobs dir. */
function assertSafeJobName(jobName: string): void {
if (!jobName || jobName === "." || jobName === ".." || /[/\\]/.test(jobName)) {
throw new Error(`invalid job name: ${jobName}`);
}
}
/** True when `pid` names a live process (signal-0 probe). */
function pidAlive(pid: number | null): boolean {
if (pid == null) return false;
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
/**
* Resolve the launch request for a new arm added to an existing experiment.
* Inherits the experiment's benchmark, dataset, and — crucially — the exact
* task sample from a sibling arm (its recorded `include`, else its observedView on GitHub (pinned to 9690622007)
Solutions
- Send only the bare job name (single path segment), e.g. 'my-run-2026-08-31', not a path
- Trim/validate the job name on the client before calling the destroy endpoint
- URL-encode the job name correctly in requests; inspect the actual value received
- Fix the caller that derives job names from file paths to strip directory components
Example fix
// before (client)
await fetch(`/destroy?job=${encodeURIComponent(fullPath)}`); // 'runs/_bench/job1' -> rejected
// after (client)
const jobName = path.basename(fullPath);
await fetch(`/destroy?job=${encodeURIComponent(jobName)}`); // 'job1' Defensive patterns
Strategy: validation
Validate before calling
function assertSafeJobName(jobName) {
if (!jobName || jobName === "." || jobName === ".." || /[/\\]/.test(jobName)) {
throw new Error(`invalid job name: ${jobName}`);
}
}
// call client-side before requesting destroy: assertSafeJobName(jobName) Type guard
function isSafeJobName(v) {
return typeof v === "string" && v.length > 0 && v !== "." && v !== ".." && !/[/\\]/.test(v);
} Try / catch
try {
await destroyRun(jobName);
} catch (err) {
if (err instanceof Error && err.message.startsWith("invalid job name:")) {
console.error(`Refusing to destroy: '${jobName}' is not a single path segment`);
return;
}
throw err;
} Prevention
- Always send the bare job name, never a path — apply path.basename() on the client
- Validate job names with the same single-segment rule before any API call
- Avoid deriving job names from user-supplied strings or URL fragments without sanitization
- Restrict job-name generation to a known-safe charset (alphanumerics, dashes)
When it happens
Trigger: An HTTP request to the destroy-run endpoint (or any caller of #destroyRun) supplies a jobName that is empty, '.', '..', or contains a slash/backslash, causing assertSafeJobName to throw.
Common situations: Client-side bug embedding a full path like 'jobs/my-run' instead of just 'my-run'; URL-encoded slashes arriving in a request param; empty job id from a stale UI reference; deliberate path-traversal attempt.
Related errors
- Invalid global daemon service name: ${JSON.stringify(service
- Destination paths cannot contain parent traversal or NUL byt
- Shared-folder destination escapes its configured root
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/50131f1f2739f67c.
Report an issue: GitHub.