RustPython/RustPython · error · ArgumentError
unexpected option string: %s
Error message
unexpected option string: %s
What it means
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.
Source
Thrown at Lib/argparse.py:2516
option_prefix, sep, explicit_arg = option_string.partition('=')
if not sep:
sep = explicit_arg = None
short_option_prefix = option_string[:2]
short_explicit_arg = option_string[2:]
for option_string in self._option_string_actions:
if option_string == short_option_prefix:
action = self._option_string_actions[option_string]
tup = action, option_string, '', short_explicit_arg
result.append(tup)
elif self.allow_abbrev and option_string.startswith(option_prefix):
action = self._option_string_actions[option_string]
tup = action, option_string, sep, explicit_arg
result.append(tup)
# shouldn't ever get here
else:
raise ArgumentError(None, _('unexpected option string: %s') % option_string)
# return the collected option tuples
return result
def _get_nargs_pattern(self, action):
# in all examples below, we have to allow for '--' args
# which are represented as '-' in the pattern
nargs = action.nargs
# if this is an optional action, -- is not allowed
option = action.option_strings
# the default (None) is assumed to be a single argument
if nargs is None:
nargs_pattern = '([A])' if option else '(-*A-*)'
# allow zero or one arguments
elif nargs == OPTIONAL:
nargs_pattern = '(A?)' if option else '(-*A?-*)'View on GitHub (pinned to aaeab4f754)
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
Example fix
# before
p = argparse.ArgumentParser(prefix_chars='+')
p.add_argument('+verbose', action='store_true')
p.parse_args(['+verbose', '-x']) # '-x' unrecognized prefix falls through
# after
p = argparse.ArgumentParser(prefix_chars='-+')
p.add_argument('-x', action='store_true')
p.parse_args(['+verbose', '-x']) Defensive patterns
Strategy: try-catch
Validate before calling
def known_tokens_only(parser, argv):
opts = set(parser._option_string_actions)
out = []
for t in argv:
if t.startswith(tuple(parser.prefix_chars)) and t not in opts and t != '--':
continue # drop tokens the parser cannot classify
out.append(t)
return out Try / catch
try:
args = parser.parse_args(argv)
except argparse.ArgumentError as e:
if str(e).startswith('unexpected option string'):
print('internal parser inconsistency:', e, file=sys.stderr)
raise SystemExit(2)
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- invalid nargs value
- .__call__() not defined
- invalid option name {option_string!r} for BooleanOptionalAct
- nargs for store actions must be != 0; if you have nothing to
- nargs must be %r to supply const
AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17).
Data as JSON: /api/errors/3b2222b6fe4ab588.
Report an issue: GitHub.