pytest-dev/pytest · error · ValueError

invalid type for ini option

Error message

invalid type for ini option {name!r}: Literal choices must be strings, got {choices!r}

What it means

Raised by _ini_type_to_member() during addini() when a Literal type is used for the type argument but not all of its choices are strings. Literal types for ini options must contain only str choices. The error names the invalid choices.

Solutions

  1. Ensure all Literal choices are strings: change Literal['fast', 42] to Literal['fast', '42']
  2. If numeric values are needed, use type='string' and validate in your plugin code
  3. Use a union of recognized scalar types (int | str) instead of Literal for mixed numeric/string input

Example fix

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

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

Strategy: type-guard

Validate before calling

from typing import get_args, get_origin, Literal
def validate_literal_choices(type_: object) -> bool:
    if get_origin(type_) is Literal:
        choices = get_args(type_)
        return all(isinstance(c, str) for c in choices)
    return True

Type guard

from typing import get_args, get_origin, Literal
def is_string_literal(type_: object) -> bool:
    if get_origin(type_) is not Literal:
        return False
    return all(isinstance(c, str) for c in get_args(type_))

Prevention

When it happens

Trigger: Calling parser.addini('mode', type=Literal['fast', 42]) — the integer 42 is not a string. The check `all(isinstance(choice, str) for choice in choices)` fails inside the get_origin(type_) is Literal branch.

Common situations: Mixing string and non-string choices in a Literal type. Using Literal[int, str] expecting numeric support. Copying a Literal from application code that includes non-string members.

Related errors


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

Appendix: source

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

def _ini_type_to_tag(name: str, type_: object) -> _IniTypeTag:
    """Normalize one member of an `addini(type=...)` argument to a string tag."""
    try:
        return _INI_TYPES[type_]
    except (KeyError, TypeError):  # TypeError: unhashable type_
        raise ValueError(
            f"invalid type for ini option {name!r}: {type_!r} (expected one of "
            f"{', '.join(repr(tag) for tag in get_args(_IniTypeTag))}, one of "
            "the types str, bool, int, float, a union of these types such as "
            "`int | str`, or a `Literal` of strings)"
        ) from None


def _ini_type_to_member(name: str, type_: object) -> _IniTypeTag | _IniLiteral:
    """Normalize one member of an `addini(type=...)` argument."""
    if get_origin(type_) is Literal:
        choices = get_args(type_)
        if not all(isinstance(choice, str) for choice in choices):
            raise ValueError(
                f"invalid type for ini option {name!r}: Literal choices "
                f"must be strings, got {choices!r}"
            )
        return _IniLiteral(choices)
    return _ini_type_to_tag(name, type_)


def _ini_type_repr(type: IniType) -> str:
    """Render an ini option type for --help output and error messages."""
    if isinstance(type, _IniLiteral):
        return " | ".join(repr(choice) for choice in type.choices)
    if isinstance(type, tuple):
        return " | ".join(_ini_type_repr(member) for member in type)
    return type


def _get_argparse_dest(opts: Sequence[str]) -> str:
    long_opts = [opt for opt in opts if opt.startswith("--")]

View on GitHub (pinned to 0d6fbdeffa)