pytest-dev/pytest · error · UsageError

: config option ' ' expects one of , got

Error message

{self.inipath}: config option '{name}' expects one of {_ini_type_repr(type)}, got {builtins.type(value).__name__}: {value!r}

What it means

Raised by _getini() at config/__init__.py:1843-1846 when an ini option registered with a union type (e.g. int | str) receives a value that matches NONE of the union members. The loop at capture.py:1836-1842 tries each member's _getini_value; if all raise TypeError/ValueError, the trailing raise at config/__init__.py:1843 fires. The outer try/except at capture.py:1847-1848 re-raises as UsageError so the user sees a clean message.

Solutions

  1. Provide a value matching one of the union members (typically a string or integer)
  2. Quote the value if you want it treated as a string in toml mode
  3. If you control the plugin, register the union more permissively (e.g. int | str | bool)
  4. Move the option to a CLI flag where argparse can do the type coercion

Example fix

// before
# pyproject.toml
[tool.pytest.ini_options]
foo = true   # addini type is int | str
// after
foo = 42     # or
foo = "true"
Defensive patterns

Strategy: validation

Validate before calling

# Before writing pyproject.toml programmatically, validate against the union
import tomllib
cfg = tomllib.loads(open('pyproject.toml').read())
opts = cfg.get('tool',{}).get('pytest',{}).get('ini_options',{})
for k, v in opts.items():
    # plugin must expose its accepted types; check docs/source
    pass

Type guard

def matches_union(value, members) -> bool:
    for m in members:
        if m == 'int' and isinstance(value, int): return True
        if m == 'str' and isinstance(value, str): return True
        if m == 'bool' and isinstance(value, bool): return True
        if m == 'float' and isinstance(value, (int, float)): return True
    return False

Try / catch

from _pytest.config import UsageError
try:
    val = config.getini('opt')
except UsageError as e:
    print(f'Bad value: {e}')

Prevention

When it happens

Trigger: A plugin registers addini('foo', type=int | str) and the user provides a value in pyproject.toml that is neither an int nor parseable as an int string (e.g. a list or a bool). The union loop fails all members and capture.py:1843 raises.

Common situations: pyproject.toml config providing a TOML list/bool where the option expects int or str; ini option type widened to a union but old configs still supplying incompatible values; toml-mode parsing surfacing native types that ini mode would have treated as strings.

Related errors


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

Appendix: source

Thrown at src/_pytest/config/__init__.py:1843

        mode = selected.mode

        # An invalid value is a user error, raised as UsageError so that it is
        # reported as a short message rather than an internal error traceback.
        try:
            if not isinstance(type, tuple):
                return self._getini_value(
                    mode, name, canonical_name, type, value, default
                )

            # Union: try each member; the first one that accepts the value wins.
            for member in type:
                try:
                    return self._getini_value(
                        mode, name, canonical_name, member, value, default
                    )
                except (TypeError, ValueError):
                    pass
            raise TypeError(
                f"{self.inipath}: config option '{name}' expects one of "
                f"{_ini_type_repr(type)}, got {builtins.type(value).__name__}: {value!r}"
            )
        except (TypeError, ValueError) as e:
            raise UsageError(str(e)) from e

    def _getini_value(
        self,
        mode: Literal["ini", "toml"],
        name: str,
        canonical_name: str,
        type: str | _IniLiteral,
        value: object,
        default: Any,
    ):
        """Convert a config value, read in the given mode, to the option's type."""
        if isinstance(type, _IniLiteral):
            # A Literal value is a plain string checked against the registered

View on GitHub (pinned to 0d6fbdeffa)