{"id":"243cb132c1850308","repo":"pytest-dev/pytest","slug":"filepath-pytest-configuration-must-be-under-a","errorCode":null,"errorMessage":"{filepath}: pytest configuration must be under a [pytest] table (found top-level options: {top_level_options})","messagePattern":"(.+?): pytest configuration must be under a \\[pytest\\] table \\(found top-level options: (.+?)\\)","errorType":"exception","errorClass":"UsageError","httpStatus":null,"severity":"error","filePath":"src/_pytest/config/findpaths.py","lineNumber":120,"sourceCode":"        toml_text = filepath.read_text(encoding=\"utf-8\")\n        try:\n            config = tomllib.loads(toml_text)\n        except tomllib.TOMLDecodeError as exc:\n            raise UsageError(f\"{filepath}: {exc}\") from exc\n\n        # pytest.toml and .pytest.toml use [pytest] table directly.\n        if filepath.name in (\"pytest.toml\", \".pytest.toml\"):\n            if \"pytest\" in config:\n                # TOML mode - preserve native TOML types.\n                return {\n                    k: ConfigValue(v, origin=\"file\", mode=\"toml\")\n                    for k, v in config[\"pytest\"].items()\n                }\n            top_level_options = [\n                key for key, value in config.items() if not isinstance(value, dict)\n            ]\n            if top_level_options:\n                raise UsageError(\n                    f\"{filepath}: pytest configuration must be under a \"\n                    f\"[pytest] table (found top-level options: \"\n                    f\"{', '.join(top_level_options)})\"\n                )\n            # \"pytest.toml\" files are always the source of configuration, even if empty.\n            return {}\n\n        # pyproject.toml uses [tool.pytest] or [tool.pytest.ini_options].\n        else:\n            tool_pytest = config.get(\"tool\", {}).get(\"pytest\", {})\n\n            # Check for toml mode config: [tool.pytest] with content outside of ini_options.\n            toml_config = {k: v for k, v in tool_pytest.items() if k != \"ini_options\"}\n            # Check for ini mode config: [tool.pytest.ini_options].\n            ini_config = tool_pytest.get(\"ini_options\", None)\n\n            if toml_config and ini_config:\n                raise UsageError(","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/config/findpaths.py#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (pytest.toml)\naddopts = '-v'\ntestpaths = ['tests']\n\n# after\n[pytest]\naddopts = '-v'\ntestpaths = ['tests']","handlingStrategy":"validation","validationCode":"import sys, pathlib\ntry:\n    import tomllib\nexcept ImportError:\n    import tomli as tomllib\n\ndef validate_pytest_toml(path: pathlib.Path) -> None:\n    config = tomllib.loads(path.read_text(encoding='utf-8'))\n    top_level = [k for k, v in config.items() if not isinstance(v, dict)]\n    if top_level:\n        raise ValueError(f'{path}: pytest config must be under [pytest]; found top-level: {top_level}')\n    if 'pytest' not in config:\n        raise ValueError(f'{path}: missing [pytest] table')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always start pytest.toml with a [pytest] table header.","Keep non-pytest tool config in pyproject.toml, not pytest.toml."],"tags":["pytest","config","toml","pyproject","config-structure","usage-error"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}