pytest-dev/pytest · error · ValueError

ini option {name!r} has a {kind} type, which has no implicit

Error message

ini option {name!r} has a {kind} type, which has no implicit default; pass an explicit `default` to `addini`

What it means

Raised by Parser.addini() when the type is a union (e.g. int | str) or a Literal of strings, but no explicit default= argument was provided. These compound types have no single sensible implicit default, so pytest refuses to guess and requires the plugin author to pass one.

Source

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

            .. versionadded:: 9.0
                The ``aliases`` parameter.

        The value of configuration keys can be retrieved via a call to
        :py:func:`config.getini(name) <pytest.Config.getini>`.
        """
        ini_type: IniType
        if type is None:
            ini_type = "string"
        elif get_origin(type) in (Union, types.UnionType):
            ini_type = tuple(
                _ini_type_to_member(name, member) for member in get_args(type)
            )
        else:
            ini_type = _ini_type_to_member(name, type)
        if default is NOTSET:
            if isinstance(ini_type, (tuple, _IniLiteral)):
                kind = "union" if isinstance(ini_type, tuple) else "Literal"
                raise ValueError(
                    f"ini option {name!r} has a {kind} type, which has no "
                    "implicit default; pass an explicit `default` to `addini`"
                )
            default = get_ini_default_for_type(ini_type)

        self._inidict[name] = (help, ini_type, default)

        for alias in aliases:
            if alias in self._inidict:
                raise ValueError(
                    f"alias {alias!r} conflicts with existing configuration option"
                )
            if (already := self._ini_aliases.get(alias)) is not None:
                raise ValueError(f"{alias!r} is already an alias of {already!r}")
            self._ini_aliases[alias] = name


def get_ini_default_for_type(type: _IniTypeTag) -> Any:

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Pass an explicit default: parser.addini('mode', type=Literal['a','b'], default='a').
  2. Choose a default that matches one of the union members / Literal choices.
  3. If you want a None default, pass default=None explicitly.

Example fix

# before
parser.addini('mode', type=Literal['fast','slow'], help='run mode')

# after
parser.addini('mode', type=Literal['fast','slow'], default='fast', help='run mode')
Defensive patterns

Strategy: validation

Validate before calling

from typing import get_origin, get_args, Literal, Union, types
def needs_default(type_: object) -> bool:
    if get_origin(type_) in (Union, types.UnionType):
        return True
    if get_origin(type_) is Literal:
        return True
    return False
# enforce: always pass default= when needs_default(type_) is True

Prevention

When it happens

Trigger: Calling parser.addini('myopt', type=int | str) or parser.addini('mode', type=Literal['a','b']) without a default= keyword. The check at line 347-353 sees default is NOTSET and ini_type is a tuple or _IniLiteral.

Common situations: Adding a new Literal/union ini option and forgetting the default; refactoring a simple-typed option into a union without adding a default.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/86d28b0d435c4a90.json. Report an issue: GitHub.