python/cpython · error · ValueError

invalid nargs value

Error message

invalid nargs value

What it means

In HelpFormatter._format_args, after handling the sentinel nargs values ('?', '*', '+', REMAINDER, PARSER, SUPPRESS), the code builds a metavar list using range(action.nargs). If nargs is a float or other non-int (e.g. 2.0 or '2'), range() raises TypeError, which is converted to ValueError('invalid nargs value'). It means the parser was constructed with an unusable nargs.

Solutions

  1. Pass an int or one of the documented sentinels: nargs=3, '?', '*', '+'.
  2. Coerce computed values: nargs=int(count).
  3. Validate config-sourced nargs before building the parser: allow only int or the four sentinel strings.

Example fix

# before
parser.add_argument('--pos', nargs=2.0)  # ValueError: invalid nargs value

# after
parser.add_argument('--pos', nargs=int(2.0))
Defensive patterns

Strategy: validation

Validate before calling

NARGS_SENTINELS = {'?', '*', '+'}

def valid_nargs(v) -> bool:
    return v in NARGS_SENTINELS or (isinstance(v, int) and not isinstance(v, bool) and v >= 0)

Type guard

def is_int_nargs(v) -> bool:
    return isinstance(v, int) and not isinstance(v, bool)

Prevention

When it happens

Trigger: argparse.add_argument('--x', nargs=1.5); nargs='two' (string); nargs=2.0 (float); a computed nargs like len(items)/2 that yields a float.

Common situations: Config-driven CLI builders converting YAML/JSON values where 2 parses as float; arithmetic on nargs counts producing floats; passing '*' vs '*' confusion or the string '3'.

Related errors


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

Appendix: source

Thrown at Lib/argparse.py:711

        elif action.nargs == ZERO_OR_MORE:
            metavar = get_metavar(1)
            if len(metavar) == 2:
                result = '[%s [%s ...]]' % metavar
            else:
                result = '[%s ...]' % metavar
        elif action.nargs == ONE_OR_MORE:
            result = '%s [%s ...]' % get_metavar(2)
        elif action.nargs == REMAINDER:
            result = '...'
        elif action.nargs == PARSER:
            result = '%s ...' % get_metavar(1)
        elif action.nargs == SUPPRESS:
            result = ''
        else:
            try:
                formats = ['%s' for _ in range(action.nargs)]
            except TypeError:
                raise ValueError("invalid nargs value") from None
            result = ' '.join(formats) % get_metavar(action.nargs)
        return result

    def _expand_help(self, action):
        help_string = str(self._get_help_string(action))
        if '%' not in help_string:
            return self._apply_text_markup(help_string)
        params = dict(vars(action), prog=self._prog)
        for name in list(params):
            value = params[name]
            if value is SUPPRESS:
                del params[name]
            elif hasattr(value, '__name__'):
                params[name] = value.__name__
        if params.get('choices') is not None:
            params['choices'] = ', '.join(map(str, params['choices']))

        t = self._theme

View on GitHub (pinned to bc6749cc3b)