{"record":{"id":"9dd040c5d7725072","repo":"pytest-dev/pytest","slug":"pytest-terminal-summary-report-not-found","errorCode":null,"errorMessage":"Pytest terminal summary report not found","messagePattern":"Pytest terminal summary report not found","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/pytester.py","lineNumber":585,"sourceCode":"        return self.parse_summary_nouns(self.outlines)\n\n    @classmethod\n    def parse_summary_nouns(cls, lines) -> dict[str, int]:\n        \"\"\"Extract the nouns from a pytest terminal summary line.\n\n        It always returns the plural noun for consistency::\n\n            ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====\n\n        Will return ``{\"failed\": 1, \"passed\": 1, \"warnings\": 1, \"errors\": 1}``.\n        \"\"\"\n        for line in reversed(lines):\n            if rex_session_duration.search(line):\n                outcomes = rex_outcome.findall(line)\n                ret = {noun: int(count) for (count, noun) in outcomes}\n                break\n        else:\n            raise ValueError(\"Pytest terminal summary report not found\")\n\n        to_plural = {\n            \"warning\": \"warnings\",\n            \"error\": \"errors\",\n        }\n        return {to_plural.get(k, k): v for k, v in ret.items()}\n\n    def assert_outcomes(\n        self,\n        passed: int = 0,\n        skipped: int = 0,\n        failed: int = 0,\n        errors: int = 0,\n        xpassed: int = 0,\n        xfailed: int = 0,\n        warnings: int | None = None,\n        deselected: int | None = None,\n    ) -> None:","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/pytester.py#L567-L603","documentation":"Thrown by RunResult.parse_summary_nouns() when scanning captured stdout lines in reverse finds no line matching the pytest session-duration regex (rex_session_duration). This means the terminal summary line pytest normally prints ('=== N passed in Xs ===') is absent from the captured output.","triggerScenarios":"Calling result.parseoutcomes() or result.assert_outcomes() on a RunResult whose stdout was truncated, redirected away from capture, or where pytest crashed before printing the summary line; running pytest with flags that suppress the summary (e.g. -q with custom conftest intercepting terminal).","commonSituations":"Pytest crashed during session finish; a plugin's pytest_terminal_summary hook raised; capture was disabled or output went to stderr only; the subprocess was killed by timeout before the summary printed.","solutions":["Inspect result.stdout.str() to see what was actually captured and why the summary line is missing.","Check the RunResult.ret exit code for a crash (e.g. ExitCode.INTERNAL_ERROR).","Ensure no -p no:terminal plugin disabled the terminal reporter.","Increase subprocess timeout if the run was cut short."],"exampleFix":"// before\nresult = pytester.runpytest_subprocess(\"-q\")\nresult.assert_outcomes(passed=1)\n// after\nresult = pytester.runpytest_subprocess(\"-q\")\nprint(result.stdout.str())\nassert result.ret == 0\nresult.assert_outcomes(passed=1)","handlingStrategy":"validation","validationCode":"result = pytester.runpytest_subprocess('-q')\nout = result.stdout.str()\nimport re\nif not re.search(r'===+ .* in ', out):\n    pytest.fail(f\"no summary line in output:\\n{out}\")\nresult.assert_outcomes(passed=1)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check result.ret before assert_outcomes.","Print captured stdout when debugging missing summaries.","Ensure the terminal reporter plugin is active."],"tags":["pytester","runresult","terminal-summary","parsing"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}