pytest-dev/pytest · error · ValueError

ini option has a type, which has no implicit default; pass…

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 during addini() when the type is a union (tuple of tags) or a Literal of strings, but no explicit default was provided. Union and Literal types have no sensible implicit default (unlike 'string' which defaults to '' or 'bool' which defaults to False), so pytest requires an explicit default= argument.

Solutions

  1. Pass an explicit default: parser.addini('mode', type=Literal['a','b'], default='a')
  2. For union types, pick a default matching one member: parser.addini('val', type=int | str, default=0)
  3. If no default makes sense, reconsider whether a union/Literal type is appropriate

Example fix

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

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

Strategy: validation

Validate before calling

from typing import get_args, get_origin, Literal, Union, types
def needs_explicit_default(type_: object) -> bool:
    if type_ is None:
        return False
    if get_origin(type_) in (Union, types.UnionType):
        return True
    if get_origin(type_) is Literal:
        return True
    return False
# Usage: if needs_explicit_default(my_type): pass default= to addini

Type guard

from typing import get_args, get_origin, Literal, Union, types
def is_union_or_literal(type_: object) -> bool:
    return get_origin(type_) in (Union, types.UnionType, Literal)

Prevention

When it happens

Trigger: Calling parser.addini('mode', type=Literal['a','b']) or parser.addini('val', type=int | str) without a default= parameter. The check `default is NOTSET and isinstance(ini_type, (tuple, _IniLiteral))` triggers.

Common situations: Adding a new ini option with a Literal or union type and forgetting the default. Refactoring an existing string option to a Literal without adding default=.

Related errors


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

Appendix: 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 0d6fbdeffa)