{"record":{"id":"861f310b0daa2355","repo":"python/cpython","slug":"invalid-choice-value-r-choose-from-choices-s","errorCode":null,"errorMessage":"invalid choice: %(value)r (choose from %(choices)s)","messagePattern":"invalid choice: %\\(value\\)r \\(choose from (.+?)\\)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"Lib/argparse.py","lineNumber":2838,"sourceCode":"            return\n\n        if isinstance(choices, str):\n            choices = iter(choices)\n\n        if value not in choices:\n            args = {'value': str(value),\n                    'choices': ', '.join(repr(str(choice)) for choice in action.choices)}\n            msg = _('invalid choice: %(value)r (choose from %(choices)s)')\n\n            if self.suggest_on_error and isinstance(value, str):\n                if all(isinstance(choice, str) for choice in action.choices):\n                    suggestions = difflib.get_close_matches(value, action.choices, 1)\n                    if suggestions:\n                        args['closest'] = suggestions[0]\n                        msg = _('invalid choice: %(value)r, maybe you meant %(closest)r? '\n                                '(choose from %(choices)s)')\n\n            raise ArgumentError(action, msg % args)\n\n    # =======================\n    # Help-formatting methods\n    # =======================\n\n    def format_usage(self, formatter=None):\n        if formatter is None:\n            formatter = self._get_formatter()\n        formatter.add_usage(self.usage, self._actions,\n                            self._mutually_exclusive_groups)\n        return formatter.format_help()\n\n    def format_help(self, formatter=None):\n        if formatter is None:\n            formatter = self._get_formatter()\n\n        # usage\n        formatter.add_usage(self.usage, self._actions,","sourceCodeStart":2820,"sourceCodeEnd":2856,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/argparse.py#L2820-L2856","documentation":"After type conversion, _check_value verifies the result against action.choices; if the value is not found, argparse raises ArgumentError listing the value and the full set of allowed choices (each str()-ed and repr'd). Choices can be any container; a string choices value is treated as an iterable of characters, which frequently surprises users.","triggerScenarios":"add_argument('--fmt', choices=['json','xml']) invoked with --fmt=yaml; a numeric choices range like list(range(1,11)) with input 12; choices='abc' accidentally passing a string, so only single characters 'a','b','c' are accepted.","commonSituations":"Typos in enum-like flags; case sensitivity ('JSON' vs 'json'); passing a string instead of a list of strings as choices; version-gated choices where a new value is rejected by an older tool build.","solutions":["Use one of the listed choices exactly (watch case and whitespace).","Normalize input with type=str.lower and list choices in lowercase.","Fix the declaration: choices must be a sequence of allowed values, e.g. choices=['a','b'], not choices='ab'.","Enable suggest_on_error (Python 3.14+) on the parser to get 'maybe you meant' hints for near-misses."],"exampleFix":"# before\nparser.add_argument('--fmt', choices=['json', 'xml'])\nparser.parse_args(['--fmt', 'JSON'])  # invalid choice: 'JSON'\n\n# after\nparser.add_argument('--fmt', choices=['json', 'xml'], type=str.lower)","handlingStrategy":"validation","validationCode":"def validate_choices(parser, argv):\n    by_name = {s: a for a in parser._actions for s in a.option_strings}\n    for i, tok in enumerate(argv):\n        name, sep, inline = tok.partition('=')\n        if name in by_name and by_name[name].choices:\n            val = inline if sep else (argv[i + 1] if i + 1 < len(argv) else None)\n            if val is not None and val not in map(str, by_name[name].choices):\n                return (name, val)\n    return None","typeGuard":null,"tryCatchPattern":"try:\n    args = parser.parse_args(argv)\nexcept argparse.ArgumentError as e:\n    if 'invalid choice' in str(e):\n        allowed = re.search(r'choose from (.*)\\)', str(e))\n        print(f'allowed values: {allowed.group(1) if allowed else \"see --help\"}')","preventionTips":["Normalize case with type=str.lower and list choices lowercase.","Pass choices as a list of strings — a bare string becomes per-character choices.","Echo the allowed set in your tool's docs so users never guess."],"tags":["argparse","cli","choices","validation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}