python/cpython · error · ArgumentError

invalid choice: %(value)r, maybe you meant %(closest)r? (cho

Error message

invalid choice: %(value)r, maybe you meant %(closest)r? (choose from %(choices)s)

What it means

Same invalid-choice check as the plain error, but when the parser was constructed with suggest_on_error=True (new in Python 3.14) and both the value and all choices are strings, argparse runs difflib.get_close_matches and, if a nearest candidate within the cutoff exists, embeds it in the message as 'maybe you meant ...?'. The raise site is identical to the non-suggesting variant in _check_value.

Source

Thrown at Lib/argparse.py:2838

            return

        if isinstance(choices, str):
            choices = iter(choices)

        if value not in choices:
            args = {'value': str(value),
                    'choices': ', '.join(repr(str(choice)) for choice in action.choices)}
            msg = _('invalid choice: %(value)r (choose from %(choices)s)')

            if self.suggest_on_error and isinstance(value, str):
                if all(isinstance(choice, str) for choice in action.choices):
                    suggestions = difflib.get_close_matches(value, action.choices, 1)
                    if suggestions:
                        args['closest'] = suggestions[0]
                        msg = _('invalid choice: %(value)r, maybe you meant %(closest)r? '
                                '(choose from %(choices)s)')

            raise ArgumentError(action, msg % args)

    # =======================
    # Help-formatting methods
    # =======================

    def format_usage(self, formatter=None):
        if formatter is None:
            formatter = self._get_formatter()
        formatter.add_usage(self.usage, self._actions,
                            self._mutually_exclusive_groups)
        return formatter.format_help()

    def format_help(self, formatter=None):
        if formatter is None:
            formatter = self._get_formatter()

        # usage
        formatter.add_usage(self.usage, self._actions,

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Use the suggested choice from the message.
  2. Keep suggest_on_error off (default) if your test suite asserts exact error text.
  3. Normalize case/whitespace via type=str.lower/.strip so near-misses collapse to exact matches.

Example fix

# before
parser = argparse.ArgumentParser(suggest_on_error=True)
parser.add_argument('--fmt', choices=['json', 'xml'])
parser.parse_args(['--fmt', 'jsn'])
# invalid choice: 'jsn', maybe you meant 'json'? (choose from 'json', 'xml')

# after
parser.parse_args(['--fmt', 'json'])
Defensive patterns

Strategy: validation

Validate before calling

def suggest(parser, value, allowed):
    import difflib
    m = difflib.get_close_matches(value, [str(c) for c in allowed], 1)
    return m[0] if m else None

# pre-validate and hint before argparse raises
hint = suggest(parser, raw_value, choices_list)

Try / catch

try:
    args = parser.parse_args(argv)
except argparse.ArgumentError as e:
    if 'invalid choice' in str(e):
        print('check the printed choices list; the message may include a suggestion')

Prevention

When it happens

Trigger: ArgumentParser(suggest_on_error=True) plus add_argument(x, choices=[...strings...]); the user supplies a string within difflib's similarity cutoff of exactly one choice (e.g. 'jsn' for 'json'). Non-string choices or suggest_on_error=False always yield the plain message.

Common situations: Human-facing CLIs where typos are common (--colour vs --color style near-misses); enabling the flag to improve UX and then seeing the extended message in captured error output; tests asserting the exact error string breaking after the flag is turned on.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/74913caedc5d2431. Report an issue: GitHub.