{"record":{"id":"cadbd03a070c65c9","repo":"pytest-dev/pytest","slug":"self-r-has-no-valid-result","errorCode":null,"errorMessage":"{self!r} has no valid result","messagePattern":"(.+?) has no valid result","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/runner.py","lineNumber":334,"sourceCode":"        *,\n        _ispytest: bool = False,\n    ) -> None:\n        check_ispytest(_ispytest)\n        self._result = result\n        self.excinfo = excinfo\n        self.start = start\n        self.stop = stop\n        self.duration = duration\n        self.when = when\n\n    @property\n    def result(self) -> TResult:\n        \"\"\"The return value of the call, if it didn't raise.\n\n        Can only be accessed if excinfo is None.\n        \"\"\"\n        if self.excinfo is not None:\n            raise AttributeError(f\"{self!r} has no valid result\")\n        # The cast is safe because an exception wasn't raised, hence\n        # _result has the expected function return type (which may be\n        #  None, that's why a cast and not an assert).\n        return cast(TResult, self._result)\n\n    @classmethod\n    def from_call(\n        cls,\n        func: Callable[[], TResult],\n        when: Literal[\"collect\", \"setup\", \"call\", \"teardown\"],\n        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,\n    ) -> CallInfo[TResult]:\n        \"\"\"Call func, wrapping the result in a CallInfo.\n\n        :param func:\n            The function to call. Called without arguments.\n        :type func: Callable[[], _pytest.runner.TResult]\n        :param when:","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/runner.py#L316-L352","documentation":"Raised by the CallInfo.result property when excinfo is not None — i.e. the wrapped call raised an exception. result is only valid for successful calls; accessing it after a failure would return an unreliable placeholder. The AttributeError (not a property of the same name) signals that there is no meaningful return value to read.","triggerScenarios":"Accessing call.result on a CallInfo produced by CallInfo.from_call when the function raised. Common in hooks like pytest_runtest_makereport that receive a CallInfo and assume success without checking excinfo.","commonSituations":"Custom pytest plugins/hooks reading call.result during setup/teardown/call phases where the call failed; report-generation code that does not guard on call.excinfo.","solutions":["Guard with excinfo: if call.excinfo is None: result = call.result.","In hooks, branch on call.when and call.excinfo before touching result.","Use call.excinfo.getrepr(...) for failure reporting instead of result."],"exampleFix":"// before\ndef pytest_runtest_makereport(item, call):\n    result = call.result  # AttributeError on failure\n// after\ndef pytest_runtest_makereport(item, call):\n    if call.excinfo is not None:\n        # failure path: use call.excinfo\n        ...\n        return\n    result = call.result","handlingStrategy":"type-guard","validationCode":"assert call.excinfo is None, f'cannot read result; call raised {call.excinfo!r}'\nresult = call.result","typeGuard":"def callinfo_has_result(call) -> bool:\n    return getattr(call, 'excinfo', None) is None","tryCatchPattern":null,"preventionTips":["Always check call.excinfo is None before reading call.result in hooks.","In pytest_runtest_makereport, branch on call.when and call.excinfo first.","Use call.excinfo.getrepr(...) for failure rendering, not result."],"tags":["pytest","runner","callinfo","plugin-api","attribute-access"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}