{"record":{"id":"3b2222b6fe4ab588","repo":"RustPython/RustPython","slug":"unexpected-option-string-s","errorCode":null,"errorMessage":"unexpected option string: %s","messagePattern":"unexpected option string: (.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"Lib/argparse.py","lineNumber":2516,"sourceCode":"            option_prefix, sep, explicit_arg = option_string.partition('=')\n            if not sep:\n                sep = explicit_arg = None\n            short_option_prefix = option_string[:2]\n            short_explicit_arg = option_string[2:]\n\n            for option_string in self._option_string_actions:\n                if option_string == short_option_prefix:\n                    action = self._option_string_actions[option_string]\n                    tup = action, option_string, '', short_explicit_arg\n                    result.append(tup)\n                elif self.allow_abbrev and option_string.startswith(option_prefix):\n                    action = self._option_string_actions[option_string]\n                    tup = action, option_string, sep, explicit_arg\n                    result.append(tup)\n\n        # shouldn't ever get here\n        else:\n            raise ArgumentError(None, _('unexpected option string: %s') % option_string)\n\n        # return the collected option tuples\n        return result\n\n    def _get_nargs_pattern(self, action):\n        # in all examples below, we have to allow for '--' args\n        # which are represented as '-' in the pattern\n        nargs = action.nargs\n        # if this is an optional action, -- is not allowed\n        option = action.option_strings\n\n        # the default (None) is assumed to be a single argument\n        if nargs is None:\n            nargs_pattern = '([A])' if option else '(-*A-*)'\n\n        # allow zero or one arguments\n        elif nargs == OPTIONAL:\n            nargs_pattern = '(A?)' if option else '(-*A?-*)'","sourceCodeStart":2498,"sourceCodeEnd":2534,"githubUrl":"https://github.com/RustPython/RustPython/blob/aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd/Lib/argparse.py#L2498-L2534","documentation":"A defensive raise at the bottom of ArgumentParser._parse_optional — the comment literally says 'shouldn't ever get here'. It fires when a token reaches the option parser yet matches none of the known branches: no exact option_strings hit, no permitted abbreviation (allow_abbrev), no '--opt=value' split, not a negative-number-looking arg, and not the '-' sentinel. Encountering it means the parser's option table or prefix configuration is inconsistent with the token being examined.","triggerScenarios":"Tokens starting with prefix_chars that fall through every branch — e.g. parsers built with unusual prefix_chars (prefix_chars='+') receiving '-like' tokens; option tables mutated behind argparse's back (manual edits to parser._option_string_actions); monkeypatched parsers in test suites that leave the registry half-updated.","commonSituations":"Custom-prefix CLIs (options like '+verbose'); test harnesses patching argparse internals; copies of parsers made via copy/deepcopy that share state inconsistently; very rarely a genuine CPython regression worth reporting upstream.","solutions":["Verify every option string was registered through add_argument — never by editing parser._option_string_actions directly","Check that prefix_chars covers the characters your options actually start with, and that options use those prefixes consistently","Reproduce on the latest Python patch release; if it persists, minimize (parser + argv) and file a bug at bugs.python.org","As a stopgap, pre-filter argv tokens that match no registered option before calling parse_args"],"exampleFix":"# before\np = argparse.ArgumentParser(prefix_chars='+')\np.add_argument('+verbose', action='store_true')\np.parse_args(['+verbose', '-x'])   # '-x' unrecognized prefix falls through\n# after\np = argparse.ArgumentParser(prefix_chars='-+')\np.add_argument('-x', action='store_true')\np.parse_args(['+verbose', '-x'])","handlingStrategy":"try-catch","validationCode":"def known_tokens_only(parser, argv):\n    opts = set(parser._option_string_actions)\n    out = []\n    for t in argv:\n        if t.startswith(tuple(parser.prefix_chars)) and t not in opts and t != '--':\n            continue  # drop tokens the parser cannot classify\n        out.append(t)\n    return out","typeGuard":null,"tryCatchPattern":"try:\n    args = parser.parse_args(argv)\nexcept argparse.ArgumentError as e:\n    if str(e).startswith('unexpected option string'):\n        print('internal parser inconsistency:', e, file=sys.stderr)\n        raise SystemExit(2)\n    raise","preventionTips":["Never mutate parser._option_string_actions directly; always use add_argument","Declare prefix_chars explicitly when options use non-'-' characters","Pin and test against the Python versions you ship on"],"tags":["argparse","cli","option-parsing","unreachable-branch","internal-invariant"],"backgroundTag":"cli-option-parsing-failed","analyzedSha":"aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd","analyzedAt":"2026-08-17T00:37:52.100Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}