pytest-dev/pytest · error · ValueError

option names already added

Error message

option names {conflict} already added

What it means

Raised by OptionGroup.addoption() when one or more option names (e.g., '--flag', '-f') in the current call conflict with names already added to the same option group. The conflict set is computed by intersecting new opts with all names of existing options in the group.

Solutions

  1. Rename the conflicting option in your addoption call to avoid the collision
  2. Guard the addoption call with a check for whether the option already exists
  3. If a plugin provides the option, remove the duplicate registration from your conftest.py
  4. Move the option to a different group if appropriate

Example fix

# before
group.addoption('--verbose', action='store_true')
# ... elsewhere in same group
group.addoption('--verbose', action='store_true')

# after
group.addoption('--verbose', action='store_true')
# remove or rename the second call
Defensive patterns

Strategy: validation

Validate before calling

def check_no_option_conflict(group, opts: list[str]) -> set[str]:
    existing = {name for opt in group.options for name in opt.names()}
    return set(opts).intersection(existing)
# Usage: conflicts = check_no_option_conflict(group, ['--verbose'])

Prevention

When it happens

Trigger: Calling group.addoption('--verbose') when '--verbose' was already added to the same group in a prior addoption call. The set intersection between new opts and existing option names is non-empty.

Common situations: Two plugins registering the same CLI flag. A conftest.py re-registering an option already added by a plugin. Copy-pasting addoption calls. Version upgrade where a plugin now registers a flag your conftest also defines.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/dc72fa8f91e88a1c. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/config/argparsing.py:457

        """Add an option to this group.

        If a shortened version of a long option is specified, it will
        be suppressed in the help. ``addoption('--twowords', '--two-words')``
        results in help showing ``--two-words`` only, but ``--twowords`` gets
        accepted **and** the automatic destination is in ``args.twowords``.

        :param opts:
            Option names, can be short or long options.
            Note that lower-case short options (e.g. `-x`) are reserved.
        :param attrs:
            Same attributes as the argparse library's :meth:`add_argument()
            <argparse.ArgumentParser.add_argument>` function accepts.
        """
        conflict = set(opts).intersection(
            name for opt in self.options for name in opt.names()
        )
        if conflict:
            raise ValueError(f"option names {conflict} already added")

        if self.parser and "dest" not in attrs:
            dest = _get_argparse_dest(opts)
            for group in self.parser._groups:
                for option in group.options:
                    if option.dest == dest:
                        raise ValueError(
                            f"option dest {dest!r} already used by "
                            f"{option.names()!r} (this is the option that maps to "
                            f"dest {dest!r}); pass dest={dest!r} explicitly "
                            "to share the destination"
                        )
        self._addoption_inner(opts, attrs, allow_reserved=False)

    def _addoption(self, *opts: str, **attrs: Any) -> None:
        """Like addoption(), but also allows registering short lower case options (e.g. -x),
        which are reserved for pytest core."""
        self._addoption_inner(opts, attrs, allow_reserved=True)

View on GitHub (pinned to 0d6fbdeffa)