pytest-dev/pytest · error · UsageError
{filepath}: pytest configuration must be under a [pytest] ta
Error message
{filepath}: pytest configuration must be under a [pytest] table (found top-level options: {top_level_options}) What it means
Raised when a pytest.toml or .pytest.toml file contains configuration keys at the top level of the TOML document instead of nested under a [pytest] table. pytest.toml requires all configuration to live under [pytest]; stray top-level scalars/arrays are rejected to prevent silent misconfiguration.
Source
Thrown at src/_pytest/config/findpaths.py:120
toml_text = filepath.read_text(encoding="utf-8")
try:
config = tomllib.loads(toml_text)
except tomllib.TOMLDecodeError as exc:
raise UsageError(f"{filepath}: {exc}") from exc
# pytest.toml and .pytest.toml use [pytest] table directly.
if filepath.name in ("pytest.toml", ".pytest.toml"):
if "pytest" in config:
# TOML mode - preserve native TOML types.
return {
k: ConfigValue(v, origin="file", mode="toml")
for k, v in config["pytest"].items()
}
top_level_options = [
key for key, value in config.items() if not isinstance(value, dict)
]
if top_level_options:
raise UsageError(
f"{filepath}: pytest configuration must be under a "
f"[pytest] table (found top-level options: "
f"{', '.join(top_level_options)})"
)
# "pytest.toml" files are always the source of configuration, even if empty.
return {}
# pyproject.toml uses [tool.pytest] or [tool.pytest.ini_options].
else:
tool_pytest = config.get("tool", {}).get("pytest", {})
# Check for toml mode config: [tool.pytest] with content outside of ini_options.
toml_config = {k: v for k, v in tool_pytest.items() if k != "ini_options"}
# Check for ini mode config: [tool.pytest.ini_options].
ini_config = tool_pytest.get("ini_options", None)
if toml_config and ini_config:
raise UsageError(View on GitHub (pinned to 98b357f69e)
Solutions
- Wrap all pytest configuration under a [pytest] table in pytest.toml.
- Move non-pytest configuration out of pytest.toml into pyproject.toml or another file.
Example fix
# before (pytest.toml) addopts = '-v' testpaths = ['tests'] # after [pytest] addopts = '-v' testpaths = ['tests']
Defensive patterns
Strategy: validation
Validate before calling
import sys, pathlib
try:
import tomllib
except ImportError:
import tomli as tomllib
def validate_pytest_toml(path: pathlib.Path) -> None:
config = tomllib.loads(path.read_text(encoding='utf-8'))
top_level = [k for k, v in config.items() if not isinstance(v, dict)]
if top_level:
raise ValueError(f'{path}: pytest config must be under [pytest]; found top-level: {top_level}')
if 'pytest' not in config:
raise ValueError(f'{path}: missing [pytest] table') Prevention
- Always start pytest.toml with a [pytest] table header.
- Keep non-pytest tool config in pyproject.toml, not pytest.toml.
When it happens
Trigger: A pytest.toml file like `addopts = '-v'` at the top level (no [pytest] header), or mixing tooling config at the top level with pytest config. The check at line 116-124 collects top-level non-dict keys and reports them.
Common situations: Migrating from pytest.ini (which uses a flat [pytest] section) to pytest.toml and forgetting to add the [pytest] table header; confusing pytest.toml semantics with pyproject.toml's [tool.pytest] nesting.
Related errors
- {filepath}: Cannot use both [tool.pytest] (native TOML types
- {filepath}: {exc}
- {self.inipath}: config option '{name}' expects one of {_ini_
- {self.inipath}: config option '{name}' expects a string, got
- {self.inipath}: config option '{name}' expects a list for ty
AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04).
Data as JSON: /data/errors/243cb132c1850308.json.
Report an issue: GitHub.