{"record":{"id":"ff26b0b121a92f95","repo":"jackwener/OpenCLI","slug":"label-must-be-one-of-choices-join","errorCode":null,"errorMessage":"${label} must be one of: ${choices.join(', ')}","messagePattern":"(.+?) must be one of: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/history.js","lineNumber":18,"sourceCode":"import { ArgumentError, EmptyResultError } from '@jackwener/opencli/errors';\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport {\n  fetchHistoryPage,\n  fetchJobStatuses,\n  getMidjourneyAccount,\n  isVideoJob,\n  jobStatusRow,\n  normalizePositiveInt,\n  promptFromFullCommand,\n} from './utils.js';\n\nconst TYPE_CHOICES = ['all', 'image', 'video'];\nconst STATUS_CHOICES = ['all', 'queued', 'running', 'completed', 'failed', 'cancelled'];\n\nfunction choice(value, fallback, choices, label) {\n  const result = String(value ?? fallback).trim().toLowerCase();\n  if (!choices.includes(result)) throw new ArgumentError(`${label} must be one of: ${choices.join(', ')}`);\n  return result;\n}\n\ncli({\n  site: 'midjourney',\n  name: 'history',\n  access: 'read',\n  description: 'List recent Midjourney image, video, and derived jobs with real lifecycle status',\n  example: 'opencli midjourney history --limit 10 --type all -f json',\n  domain: 'www.midjourney.com',\n  strategy: Strategy.COOKIE,\n  browser: true,\n  siteSession: 'persistent',\n  navigateBefore: 'https://www.midjourney.com/imagine',\n  args: [\n    { name: 'limit', type: 'int', default: 10, help: 'Number of matching jobs (1..100)' },\n    { name: 'type', default: 'all', help: 'all, image, or video' },\n    { name: 'status', default: 'all', help: 'all, queued, running, completed, failed, or cancelled' },","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/history.js#L1-L36","documentation":"ArgumentError thrown by the `choice` helper in `opencli midjourney history` when the --type or --status flag value is not one of the allowed enumerated values after trimming and lowercasing. The library validates filter inputs against fixed choice lists (TYPE_CHOICES = all/image/video; STATUS_CHOICES = all/queued/running/completed/failed/cancelled) before making any network request, so invalid filters fail fast.","triggerScenarios":"`opencli midjourney history --type image2` or `--type Image` is fine (lowercased) but `--type vid` (not exact) fails; `--status complete` instead of `--status completed`; `--status cancel` instead of `--status cancelled`; passing an empty or non-string value that normalizes outside the lists.","commonSituations":"Typos or truncated enum values on the command line; scripting the CLI with user input that isn't validated; assuming singular forms ('image' is valid but 'complete'/'cancel' abbreviations of status are not); confusing 'cancelled' (double-l) spelling with US 'canceled'.","solutions":["Use exactly one of the listed values: --type all|image|video and --status all|queued|running|completed|failed|cancelled.","Check spelling, especially 'completed' (not 'complete') and 'cancelled' (not 'canceled' on input).","Read the help output (`opencli midjourney history --help`) for the accepted values.","Validate user-supplied filter values in wrapper scripts before invoking the CLI."],"exampleFix":"// before\nopencli midjourney history --status complete --type vid\n// after\nopencli midjourney history --status completed --type video","handlingStrategy":"validation","validationCode":"const TYPE_CHOICES = ['all', 'image', 'video'];\nconst STATUS_CHOICES = ['all', 'queued', 'running', 'completed', 'failed', 'cancelled'];\nfunction assertChoice(label, value, choices) {\n  const v = String(value ?? '').trim().toLowerCase();\n  if (!choices.includes(v)) throw new Error(`${label} must be one of: ${choices.join(', ')} (got \"${value}\")`);\n  return v;\n}\nconst type = assertChoice('--type', 'video', TYPE_CHOICES);\nconst status = assertChoice('--status', 'completed', STATUS_CHOICES);","typeGuard":"function isHistoryType(v) { return ['all','image','video'].includes(v); }\nfunction isHistoryStatus(v) { return ['all','queued','running','completed','failed','cancelled'].includes(v); }","tryCatchPattern":"import { ArgumentError } from '@jackwener/opencli/errors';\ntry {\n  await opencli.midjourney.history({ type, status });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be one of/.test(e.message)) {\n    console.error(`Bad filter: ${e.message}`); process.exitCode = 2; return;\n  }\n  throw e;\n}","preventionTips":["Validate enum inputs against the same choice lists the CLI uses, before invoking.","Never abbreviate status values — use the exact spellings 'completed' and 'cancelled'.","Normalize user input with .trim().toLowerCase() before passing it through.","In scripts, source choices from a shared constant rather than hardcoding strings at call sites."],"tags":["validation","argument-error","cli","enum"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}