{"id":"378cc8ef232b453c","repo":"tj/commander.js","slug":"allowed-choices-are-this-argchoices-join","errorCode":null,"errorMessage":"Allowed choices are ${this.argChoices.join(', ')}.","messagePattern":"Allowed choices are (.+?)\\.","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"lib/argument.js","lineNumber":102,"sourceCode":"   */\n\n  argParser(fn) {\n    this.parseArg = fn;\n    return this;\n  }\n\n  /**\n   * Only allow argument value to be one of choices.\n   *\n   * @param {string[]} values\n   * @return {Argument}\n   */\n\n  choices(values) {\n    this.argChoices = values.slice();\n    this.parseArg = (arg, previous) => {\n      if (!this.argChoices.includes(arg)) {\n        throw new InvalidArgumentError(\n          `Allowed choices are ${this.argChoices.join(', ')}.`,\n        );\n      }\n      if (this.variadic) {\n        return this._collectValue(arg, previous);\n      }\n      return arg;\n    };\n    return this;\n  }\n\n  /**\n   * Make argument required.\n   *\n   * @returns {Argument}\n   */\n  argRequired() {\n    this.required = true;","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/tj/commander.js/blob/ba6d13ddb4243e5913367734f8c159089ffe7834/lib/argument.js#L84-L120","documentation":"Thrown by Argument.choices() (lib/argument.js:100-105) when an argument value is not in the configured allow-list. The choices() method installs a parseArg that calls Array.includes on this.argChoices; a miss throws InvalidArgumentError listing the allowed values. This is the argument-side analogue of option choices.","triggerScenarios":"Calling `.argument('<mode>', 'mode', null).choices(['a','b','c'])` then running the program with a value outside ['a','b','c'], e.g. `prog d`. Each variadic value is checked individually, so `prog a x b` also fails on 'x'.","commonSituations":"Typos in the supplied value; case mismatch ('A' vs 'a' — choices is case-sensitive); a new valid choice added to the backend but not to the choices() list; upstream script forwarding an unfiltered env value.","solutions":["Pass one of the listed allowed values.","If the value should be valid, add it to the choices() array.","Normalize input case before choices() — e.g. `.choices(['a','b'])` plus a parser that lowercases — or call choices with the full set including upper-case variants.","Surface the allowed set in --help by describing it in the argument description string."],"exampleFix":"// before\n.argument('<mode>', 'mode').choices(['read', 'write'])\n// prog READ -> throws\n\n// after\n.argument('<mode>', 'mode', (v) => v.toLowerCase()).choices(['read', 'write'])","handlingStrategy":"validation","validationCode":"const ALLOWED = ['read', 'write', 'append'];\nconst value = process.argv[idx];\nif (!ALLOWED.includes(value)) {\n  console.error(`mode must be one of: ${ALLOWED.join(', ')}`);\n  process.exit(2);\n}","typeGuard":"const MODES = ['read', 'write', 'append'] as const;\ntype Mode = typeof MODES[number];\nfunction isMode(v: unknown): v is Mode {\n  return typeof v === 'string' && (MODES as readonly string[]).includes(v);\n}","tryCatchPattern":"try { await program.parseAsync(); }\ncatch (e) {\n  if (e.code === 'commander.invalidArgument' && /Allowed choices/.test(e.message)) {\n    console.error(e.message); process.exit(2);\n  }\n  throw e;\n}","preventionTips":["Derive the choices array from a single shared const so help, validation, and TS types stay in sync.","Normalize case before choices() if users commonly vary capitalization.","Echo the allowed set in the argument description for --help."],"tags":["commander","argument-parsing","choices","validation","cli"],"analyzedSha":"ba6d13ddb4243e5913367734f8c159089ffe7834","analyzedAt":"2026-08-03T20:26:04.326Z","schemaVersion":2}