{"record":{"id":"e308d6775589ef78","repo":"pytest-dev/pytest","slug":"path-pytest-configuration-must-be-under-a-pyte","errorCode":null,"errorMessage":"{path}: 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":146,"sourceCode":") -> ConfigDict | None:\n    \"\"\"Return the configuration in the ``[pytest]`` table of a parsed TOML\n    document, or None if it has none.\n\n    Raise UsageError for options written outside of any table, which is the\n    usual way of getting the table wrong.\n    \"\"\"\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()  # type: ignore[attr-defined]\n        }\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\"{path}: pytest configuration must be under a \"\n            f\"[pytest] table (found top-level options: \"\n            f\"{', '.join(top_level_options)})\"\n        )\n    return None\n\n\ndef _config_from_tool_pytest(\n    path: Path, config: dict[str, object]\n) -> ConfigDict | None:\n    \"\"\"Return the configuration in the ``[tool.pytest]`` tables of a parsed\n    TOML document, or None if it has none.\"\"\"\n    tool_pytest = config.get(\"tool\", {}).get(\"pytest\", {})  # type: ignore[attr-defined]\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)","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/config/findpaths.py#L128-L164","documentation":"When pytest encounters a [pytest] table in a TOML file, it expects all pytest settings to live inside that table. If, instead, top-level scalar keys sit beside (or instead of) the [pytest] table, pytest rejects them. This guards against the common mistake of writing 'addopts = ...' at the top of a pyproject-style file without a parent table.","triggerScenarios":"A pytest.ini-style flat key (e.g. 'addopts = ...') placed at the document root of a .toml file; a [pytest] table present AND stray top-level option keys next to it.","commonSituations":"Renaming pytest.ini to pytest.toml without wrapping keys under [pytest]; mixing pytest.ini and pyproject.toml conventions in one file.","solutions":["Indent/move every reported top-level option under a [pytest] table header in that file.","If the file is pyproject.toml, move the options under [tool.pytest.ini_options] instead of leaving them at the top level.","Remove keys that do not belong to pytest entirely."],"exampleFix":"// before\naddopts = [\"-ra\"]\ntestpaths = [\"tests\"]\n// after\n[pytest]\naddopts = [\"-ra\"]\ntestpaths = [\"tests\"]","handlingStrategy":"validation","validationCode":"import tomllib, pathlib\n\ndef validate_pytest_table(path: str) -> None:\n    doc = tomllib.loads(pathlib.Path(path).read_text())\n    top = [k for k, v in doc.items() if not isinstance(v, dict)]\n    assert not top, f'top-level options must move under [pytest]: {top}'","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Always nest pytest options under [pytest] (in pytest.toml) or [tool.pytest.ini_options] (in pyproject.toml).","Review config after renaming files between pytest.ini/pytest.toml/pyproject.toml."],"tags":["config","toml","pyproject","schema"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}