python/cpython · error · ValueError

invalid option name {option_string!r} for BooleanOptionalAct

Error message

invalid option name {option_string!r} for BooleanOptionalAction

What it means

BooleanOptionalAction auto-generates a negated form for each option ('--foo' -> '--no-foo'). For two-dash options it refuses names that already start with 'no-' after the dashes ('--no-x'), because the generated negative would collide or be nonsensical ('--no-no-x'). The ValueError is raised at construction time, i.e. when the parser is built.

Source

Thrown at Lib/argparse.py:1063

class BooleanOptionalAction(Action):
    def __init__(self,
                 option_strings,
                 dest,
                 default=None,
                 required=False,
                 help=None,
                 deprecated=False):

        _option_strings = []
        neg_option_strings = []
        for option_string in option_strings:
            _option_strings.append(option_string)

            if len(option_string) > 2 and option_string[0] == option_string[1]:
                # two-dash long option: '--foo' -> '--no-foo'
                if option_string.startswith('no-', 2):
                    raise ValueError(f'invalid option name {option_string!r} '
                                     f'for BooleanOptionalAction')
                option_string = option_string[:2] + 'no-' + option_string[2:]
                _option_strings.append(option_string)
                neg_option_strings.append(option_string)
            elif len(option_string) > 2 and option_string[0] != option_string[1]:
                # single-dash long option: '-foo' -> '-nofoo'
                if option_string.startswith('no', 1):
                    raise ValueError(f'invalid option name {option_string!r} '
                                     f'for BooleanOptionalAction')
                option_string = option_string[:1] + 'no' + option_string[1:]
                _option_strings.append(option_string)
                neg_option_strings.append(option_string)

        super().__init__(
            option_strings=_option_strings,
            dest=dest,
            nargs=0,
            default=default,

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Drop the 'no-' prefix and let the action generate it: add_argument('--cache', action=BooleanOptionalAction) gives --cache/--no-cache.
  2. If the flag must keep its literal name, use a plain store_true/store_false action instead.
  3. Strip 'no-' prefixes when generating options from config keys before passing them to add_argument.

Example fix

# before
parser.add_argument('--no-cache', action=argparse.BooleanOptionalAction)  # ValueError at parser build

# after
parser.add_argument('--cache', action=argparse.BooleanOptionalAction)  # provides --cache and --no-cache
Defensive patterns

Strategy: validation

Validate before calling

def ok_boolean_option_name(name: str) -> bool:
    if name.startswith('--'):
        return not name.startswith('--no-')
    return True

Prevention

When it happens

Trigger: parser.add_argument('--no-cache', action=argparse.BooleanOptionalAction); any long option string beginning '--no-'; programmatically prefixed option names from a config loop.

Common situations: Teams naming flags with a built-in negative ('--no-color', '--no-cache') and then adding BooleanOptionalAction for it; wrapping existing negated flags with the new action during a CLI modernization.

Related errors


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