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 {value!r} What it means
Raised by _getini_value() at config/__init__.py:1868-1872 when an ini option registered with a Literal[...] type receives a string that is NOT one of the registered choices. _IniLiteral.choices (argparsing.py:50) holds the allowed strings; capture.py:1868 checks membership and raises ValueError, which is converted to UsageError by the outer handler at config/__init__.py:1847-1848. _ini_type_repr() (argparsing.py:95) renders the allowed choices in the message.
Solutions
- Use one of the choices listed in the error message (rendered by _ini_type_repr)
- Check the plugin's changelog if a previously valid choice was removed
- Match case exactly - Literal membership is case-sensitive string equality
- If you need more flexibility, request the plugin author switch from Literal to a free-form string with custom validation
Example fix
// before # pytest.ini [pytest] log_cli_level = VERBOSE // after [pytest] log_cli_level = DEBUG
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = {'DEBUG','INFO','WARNING','ERROR','CRITICAL'} # example
if value not in ALLOWED:
raise ValueError(f'log_level must be one of {ALLOWED}, got {value!r}') Type guard
def in_choices(value: str, choices) -> bool:
return value in choices Try / catch
from _pytest.config import UsageError
try:
val = config.getini('mode')
except UsageError as e:
# message lists valid choices via _ini_type_repr
print(e) Prevention
- Copy choice values verbatim from plugin docs
- Match case exactly - Literal membership is case-sensitive
- When upgrading plugins, re-check the Literal choices in their changelog
When it happens
Trigger: A plugin registers addini('mode', type=Literal['a','b','c']) and the user writes `mode = 'd'`. capture.py:1868 returns False for membership and capture.py:1869-1872 raises.
Common situations: Typos in option values; using a value valid in an older plugin version that was since removed from the Literal; case-sensitivity mismatches (e.g. 'Debug' vs 'DEBUG'); copy-paste from another tool's config.
Related errors
- : config option ' ' expects a string, got
- ' ' is not recognized as a logging level name for ' '…
- : config option ' ' expects one of , got
- tmp_path_retention_count must be >= 0. Current input
- Expected a float string for option
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/aaeea2877ccafd18.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/config/__init__.py:1869
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
# choices, without coercion, in both ini and toml modes.
if not isinstance(value, str):
raise TypeError(
f"{self.inipath}: config option '{name}' expects a string, "
f"got {builtins.type(value).__name__}: {value!r}"
)
if value not in type.choices:
raise ValueError(
f"{self.inipath}: config option '{name}' expects one of "
f"{_ini_type_repr(type)}, got {value!r}"
)
return value
if mode == "ini":
# In ini mode, values are always str | list[str].
assert isinstance(value, (str, list))
return self._getini_ini(name, canonical_name, type, value, default)
elif mode == "toml":
return self._getini_toml(name, canonical_name, type, value, default)
else:
assert_never(mode)
def _getini_ini(
self,
name: str,
canonical_name: str,
type: str,View on GitHub (pinned to 0d6fbdeffa)