can1357/oh-my-pi · error · Error

arm must be a non-empty [A-Za-z0-9_.-] token

Error message

arm must be a non-empty [A-Za-z0-9_.-] token

What it means

resolveArmLaunch validates the requested arm name before building a LaunchRequest. An arm name is used verbatim in the job name (`${experimentId}-${arm}`), which becomes a filesystem directory, so it must be a non-empty token limited to [A-Za-z0-9_.-]. This error means the `arm` field of the AddArmRequest was missing, empty, or contained forbidden characters.

Source

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

	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 observed
 * trial tasks) so the arm is directly comparable. Only per-arm knobs (model,
 * prewalk, role, note, extra args) come from `req`. Throws if the experiment has
 * no runs to inherit from or the arm name is taken.
 */
export function resolveArmLaunch(store: RunStore, experimentId: string, req: AddArmRequest): LaunchRequest {
	if (!req.arm || /[^\w.-]/.test(req.arm)) throw new Error("arm must be a non-empty [A-Za-z0-9_.-] token");
	if (!req.model) throw new Error("model is required");
	const siblings = store.listRuns().filter(r => experimentOf(r.jobName) === experimentId);
	if (siblings.length === 0) throw new Error(`experiment '${experimentId}' has no runs to inherit from`);
	// Template = the sibling whose recorded `include` list is the longest (the
	// fullest expression of the experiment's sample — partial re-run arms
	// record subsets); among include-less siblings, the most observed trials.
	// listRuns is newest-first so ties keep the newest.
	const strings = (v: unknown): string[] =>
		Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
	const recordedInclude = (r: RunRow): string[] => strings((r.config as Partial<LaunchRequest>).include);
	const score = (r: RunRow): [number, number] => {
		const recorded = recordedInclude(r).length;
		return recorded > 0 ? [1, recorded] : [0, store.listTraces(r.jobName).length];
	};
	let template = siblings[0];
	let templateScore = score(template);
	for (const r of siblings.slice(1)) {
		const s = score(r);

View on GitHub (pinned to 9690622007)

Solutions

  1. Set req.arm to a non-empty string containing only letters, digits, underscore, dot, or hyphen (e.g. 'baseline-v2').
  2. If deriving the arm from a model id, slugify it first: arm.replace(/[^\w.-]/g, '-').
  3. Validate client-side before calling: if (!arm || /[^\w.-]/.test(arm)) throw.
  4. Check that the arm field name is spelled correctly and not shadowed by undefined in the request object.

Example fix

// before
await server.addArm(expId, { arm: `${model}` , model }); // model='openai/gpt-4o'
// after
const arm = model.replace(/[^\w.-]/g, "-"); // 'openai-gpt-4o'
await server.addArm(expId, { arm, model });
Defensive patterns

Strategy: validation

Validate before calling

function validArm(arm: unknown): arm is string {
  return typeof arm === "string" && arm.length > 0 && !/[^\w.-]/.test(arm);
}
if (!validArm(req.arm)) throw new Error("arm must match [A-Za-z0-9_.-]");

Type guard

const isSafeToken = (v: unknown): v is string =>
  typeof v === "string" && /^[A-Za-z0-9_.-]+$/.test(v);

Try / catch

try {
  server.addArm(expId, req);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("arm must be")) {
    req.arm = req.arm?.replace(/[^\w.-]/g, "-") || "arm-1";
    server.addArm(expId, req);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling resolveArmLaunch (directly or via addArm) with req.arm undefined, empty string '', or a name containing whitespace, '/', ':', or other non-[\w.-] characters — e.g. { arm: 'gpt/5', model: 'x' } or { model: 'x' } with no arm at all.

Common situations: Interpolating a model id with slashes (e.g. 'openai/gpt-4o') into the arm name; forgetting to set the arm field when constructing the request programmatically; pasting a label with spaces or colons from a dashboard.

Related errors


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