pytest-dev/pytest · error · UsageError
: config option ' ' expects a string, got
Error message
{self.inipath}: config option '{name}' expects a string, got {builtins.type(value).__name__}: {value!r} What it means
Raised by _getini_value() at config/__init__.py:1864-1867 when an ini option registered with a Literal[...] type (normalized to _IniLiteral, argparsing.py:47) receives a non-string value. The check at capture.py:1860-1867 enforces that Literal choices are matched against a plain string in both ini and toml modes; passing a TOML int/bool/list triggers TypeError, which is then converted to UsageError by the outer handler at config/__init__.py:1847-1848.
Solutions
- Quote the value so it is parsed as a TOML string: mode = "a"
- Pick one of the documented Literal choices
- If you control the plugin, broaden the type to Literal[...] | str or document the allowed strings clearly
Example fix
// before # pyproject.toml [tool.pytest.ini_options] log_level = 1 # Literal['DEBUG','INFO',...] // after log_level = "DEBUG"
Defensive patterns
Strategy: validation
Validate before calling
# Ensure choice-typed ini values are TOML strings
import tomllib
cfg = tomllib.loads(open('pyproject.toml').read())
for k, v in cfg.get('tool',{}).get('pytest',{}).get('ini_options',{}).items():
if not isinstance(v, str) and not isinstance(v, (list,)):
print(f'{k} should be a string, got {type(v).__name__}') Type guard
def is_str_or_list(v) -> bool:
return isinstance(v, str) or (isinstance(v, list) and all(isinstance(x, str) for x in v)) Try / catch
from _pytest.config import UsageError
try:
val = config.getini('mode')
except UsageError as e:
print(f'Set mode to a quoted string in pyproject.toml: {e}') Prevention
- Always quote choice values in pyproject.toml even when they look numeric
- Document Literal options with explicit TOML examples in plugin docs
- Validate pyproject.toml [tool.pytest.ini_options] in a pre-commit hook
When it happens
Trigger: A plugin registers addini('mode', type=Literal['a','b','c']) and the user writes `mode = 1` or `mode = true` in pyproject.toml. The non-string check at capture.py:1863 raises TypeError.
Common situations: TOML-native values for choice options (where the user assumed the choice would accept any truthy value or numeric key); migrating ini configs to pyproject.toml and losing the implicit string quoting; plugin docs not making clear that the option is choice-constrained.
Related errors
- : config option ' ' expects one of , got
- : config option ' ' expects one of , got
- ' ' is not recognized as a logging level name for ' '…
- 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/8587a2c455b25ae2.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/config/__init__.py:1864
)
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
# 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)
View on GitHub (pinned to 0d6fbdeffa)