{"record":{"id":"c928c2dd829a635c","repo":"can1357/oh-my-pi","slug":"arm-must-be-a-non-empty-a-za-z0-9-token","errorCode":null,"errorMessage":"arm must be a non-empty [A-Za-z0-9_.-] token","messagePattern":"arm must be a non-empty \\[A-Za-z0-9_\\.-\\] token","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/server.ts","lineNumber":120,"sourceCode":"\tif (pid == null) return false;\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Resolve the launch request for a new arm added to an existing experiment.\n * Inherits the experiment's benchmark, dataset, and — crucially — the exact\n * task sample from a sibling arm (its recorded `include`, else its observed\n * trial tasks) so the arm is directly comparable. Only per-arm knobs (model,\n * prewalk, role, note, extra args) come from `req`. Throws if the experiment has\n * no runs to inherit from or the arm name is taken.\n */\nexport function resolveArmLaunch(store: RunStore, experimentId: string, req: AddArmRequest): LaunchRequest {\n\tif (!req.arm || /[^\\w.-]/.test(req.arm)) throw new Error(\"arm must be a non-empty [A-Za-z0-9_.-] token\");\n\tif (!req.model) throw new Error(\"model is required\");\n\tconst siblings = store.listRuns().filter(r => experimentOf(r.jobName) === experimentId);\n\tif (siblings.length === 0) throw new Error(`experiment '${experimentId}' has no runs to inherit from`);\n\t// Template = the sibling whose recorded `include` list is the longest (the\n\t// fullest expression of the experiment's sample — partial re-run arms\n\t// record subsets); among include-less siblings, the most observed trials.\n\t// listRuns is newest-first so ties keep the newest.\n\tconst strings = (v: unknown): string[] =>\n\t\tArray.isArray(v) ? v.filter((x): x is string => typeof x === \"string\") : [];\n\tconst recordedInclude = (r: RunRow): string[] => strings((r.config as Partial<LaunchRequest>).include);\n\tconst score = (r: RunRow): [number, number] => {\n\t\tconst recorded = recordedInclude(r).length;\n\t\treturn recorded > 0 ? [1, recorded] : [0, store.listTraces(r.jobName).length];\n\t};\n\tlet template = siblings[0];\n\tlet templateScore = score(template);\n\tfor (const r of siblings.slice(1)) {\n\t\tconst s = score(r);","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/server.ts#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set req.arm to a non-empty string containing only letters, digits, underscore, dot, or hyphen (e.g. 'baseline-v2').","If deriving the arm from a model id, slugify it first: arm.replace(/[^\\w.-]/g, '-').","Validate client-side before calling: if (!arm || /[^\\w.-]/.test(arm)) throw.","Check that the arm field name is spelled correctly and not shadowed by undefined in the request object."],"exampleFix":"// before\nawait server.addArm(expId, { arm: `${model}` , model }); // model='openai/gpt-4o'\n// after\nconst arm = model.replace(/[^\\w.-]/g, \"-\"); // 'openai-gpt-4o'\nawait server.addArm(expId, { arm, model });","handlingStrategy":"validation","validationCode":"function validArm(arm: unknown): arm is string {\n  return typeof arm === \"string\" && arm.length > 0 && !/[^\\w.-]/.test(arm);\n}\nif (!validArm(req.arm)) throw new Error(\"arm must match [A-Za-z0-9_.-]\");","typeGuard":"const isSafeToken = (v: unknown): v is string =>\n  typeof v === \"string\" && /^[A-Za-z0-9_.-]+$/.test(v);","tryCatchPattern":"try {\n  server.addArm(expId, req);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"arm must be\")) {\n    req.arm = req.arm?.replace(/[^\\w.-]/g, \"-\") || \"arm-1\";\n    server.addArm(expId, req);\n  } else throw err;\n}","preventionTips":["Slugify any external id (model ids, labels) before using it as an arm name.","Keep a shared regex constant for arm names and validate at the UI/CLI boundary.","Never embed raw model ids containing '/' in arm names.","Unit-test arm-name sanitization with the exact rejection regex."],"tags":["validation","input-sanitization","naming"],"backgroundTag":"invalid-identifier-token","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}