{"record":{"id":"716b373df041c3fd","repo":"can1357/oh-my-pi","slug":"flag-must-be-a-positive-integer","errorCode":null,"errorMessage":"${flag} must be a positive integer","messagePattern":"(.+?) must be a positive integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/tb/cli.ts","lineNumber":211,"sourceCode":"\t\t\t\tconfig.help = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`unknown option: ${arg}`);\n\t\t}\n\t}\n\n\tif (config.help) return config;\n\tif (config.models.length === 0) throw new Error(\"at least one --model is required\");\n\tfor (const model of config.models) {\n\t\tconst slash = model.indexOf(\"/\");\n\t\tif (slash <= 0 || slash === model.length - 1) throw new Error(`invalid model ${model}; expected provider/model`);\n\t}\n\tfor (const [flag, value] of [\n\t\t[\"--attempts\", config.attempts],\n\t\t[\"--concurrency\", config.concurrency],\n\t\t[\"--epochs\", config.epochs],\n\t] as const) {\n\t\tif (!Number.isInteger(value) || value < 1) throw new Error(`${flag} must be a positive integer`);\n\t}\n\tif (config.budget !== null && (!Number.isFinite(config.budget) || config.budget < 0)) {\n\t\tthrow new Error(\"--budget must be a non-negative number\");\n\t}\n\treturn config;\n}\n\nclass Semaphore {\n\t#available: number;\n\t#queue: Array<() => void> = [];\n\n\tconstructor(available: number) {\n\t\tthis.#available = available;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tif (this.#available > 0) {\n\t\t\tthis.#available--;","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/tb/cli.ts#L193-L229","documentation":"parseArgs validates --attempts, --concurrency, and --epochs: each must be an integer >= 1. Non-integers (floats, NaN from a non-numeric string) or values below 1 throw this error naming the offending flag.","triggerScenarios":"--attempts 0, --concurrency 2.5, --epochs abc (parses to NaN), or a negative number; also values supplied via inline `--epochs=0` form.","commonSituations":"Shell variables that are empty or contain units ('3x'); copy-pasted fractional values; misunderstanding that 0 means 'unlimited' (it does not — minimum is 1).","solutions":["Pass a whole number >= 1 for the named flag","Remove units/suffixes from the value (3 not 3x)","Check the shell variable actually contains a number"],"exampleFix":"// before\n$ tb --epochs 0 --model x/y\n// after\n$ tb --epochs 1 --model x/y","handlingStrategy":"validation","validationCode":"const isPositiveInt = (v: unknown): v is number =>\n  typeof v === \"number\" && Number.isInteger(v) && v >= 1;\nif (!isPositiveInt(epochs)) throw new Error(\"--epochs must be >= 1\");","typeGuard":"const isPositiveInt = (v: unknown): v is number =>\n  typeof v === \"number\" && Number.isInteger(v) && v >= 1;","tryCatchPattern":"try {\n  config = parseArgs(args);\n} catch (err) {\n  if (String(err.message).endsWith(\"must be a positive integer\")) {\n    console.error(`${err.message} — pass a whole number >= 1`);\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Strip units/suffixes from numeric shell variables","Remember minimum is 1, not 0","Validate numbers from config files before forwarding to the CLI"],"tags":["cli","validation","arguments"],"backgroundTag":"invalid-numeric-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}