{"record":{"id":"aacee1bed607c05a","repo":"jackwener/OpenCLI","slug":"label-must-be-a-positive-integer-aacee1","errorCode":null,"errorMessage":"${label} must be a positive integer","messagePattern":"(.+?) must be a positive integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/utils.js","lineNumber":53,"sourceCode":"\nexport function unwrapEvaluateResult(payload) {\n  if (payload && !Array.isArray(payload) && typeof payload === 'object' && 'session' in payload && 'data' in payload) {\n    return payload.data;\n  }\n  return payload;\n}\n\nexport function normalizeBoolean(value, fallback = false) {\n  if (typeof value === 'boolean') return value;\n  if (value == null || value === '') return fallback;\n  const normalized = String(value).trim().toLowerCase();\n  return ['true', '1', 'yes', 'on'].includes(normalized);\n}\n\nexport function normalizePositiveInt(value, fallback, max, label) {\n  const parsed = value == null || value === '' ? fallback : Number(value);\n  if (!Number.isInteger(parsed) || parsed < 1) {\n    throw new ArgumentError(`${label} must be a positive integer`);\n  }\n  if (parsed > max) {\n    throw new ArgumentError(`${label} must be <= ${max}`);\n  }\n  return parsed;\n}\n\nexport function parseJobId(value) {\n  const raw = String(value ?? '').trim();\n  if (UUID_RE.test(raw)) return raw.toLowerCase();\n  try {\n    const parsed = new URL(raw);\n    const match = parsed.pathname.match(/^\\/jobs\\/([0-9a-f-]{36})\\/?$/i);\n    if (parsed.protocol === 'https:' && parsed.hostname === MIDJOURNEY_DOMAIN && match && UUID_RE.test(match[1])) {\n      return match[1].toLowerCase();\n    }\n  } catch {}\n  throw new ArgumentError(","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/utils.js#L35-L71","documentation":"ArgumentError thrown by normalizePositiveInt in clis/midjourney/utils.js when a numeric option (e.g. --timeout, --limit) is not an integer >= 1, or exceeds its maximum. The helper applies a fallback when the value is null/empty, otherwise coerces with Number() and validates Number.isInteger(parsed) && parsed >= 1, then enforces parsed <= max.","triggerScenarios":"`opencli midjourney login --timeout 0` or `--timeout -5`; `opencli midjourney history --limit 2.5` (non-integer); `--limit abc` (NaN fails Number.isInteger); passing a value above the per-arg max, e.g. --timeout 1000 (>900) or --limit 500 (>100).","commonSituations":"Shell variables that are empty strings expanded into flags; copy-pasting limits from docs of other tools; decimal typing ('1.5 minutes'); forgetting the documented cap (timeout 1..900, limit 1..100); passing '--limit \"\"' which falls back correctly but '--limit 0' fails.","solutions":["Pass a whole number >= 1 and within the max: --timeout 1..900, --limit 1..100.","Quote/validate shell variables: use \"${LIMIT:-10}\" and check it's a positive integer before invoking.","Remove the flag entirely to use the built-in default (timeout 300, limit 10) instead of passing 0 or empty.","Check the command's --help for each numeric argument's documented range."],"exampleFix":"// before\nopencli midjourney history --limit 0\n// after\nopencli midjourney history --limit 25\n# or omit the flag for the default (10):\nopencli midjourney history","handlingStrategy":"validation","validationCode":"function toPositiveInt(value, { max, fallback, label }) {\n  if (value == null || value === '') return fallback;\n  const n = Number(value);\n  if (!Number.isInteger(n) || n < 1) throw new Error(`${label} must be a positive integer (got \"${value}\")`);\n  if (max != null && n > max) throw new Error(`${label} must be <= ${max}`);\n  return n;\n}\nconst limit = toPositiveInt(process.env.MJ_LIMIT, { max: 100, fallback: 10, label: '--limit' });\nconst timeout = toPositiveInt(process.env.MJ_TIMEOUT, { max: 900, fallback: 300, label: '--timeout' });","typeGuard":"function isPositiveInt(v) { return Number.isInteger(v) && v >= 1; }","tryCatchPattern":"import { ArgumentError } from '@jackwener/opencli/errors';\ntry {\n  await opencli.midjourney.history({ limit });\n} catch (e) {\n  if (e instanceof ArgumentError && /positive integer|must be <=/.test(e.message)) {\n    console.error(`Bad option: ${e.message}`); process.exitCode = 2; return;\n  }\n  throw e;\n}","preventionTips":["Coerce and validate numeric CLI/env inputs with Number() + Number.isInteger before passing them.","Remember the documented caps: --timeout 1..900, --limit 1..100.","Handle empty shell variables explicitly (\"${VAR:-default}\") so blank expansions don't become '0' or garbage.","Never pass fractional or zero values — omit the flag to get the built-in default instead."],"tags":["validation","argument-error","cli","numeric-input"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}