pytest-dev/pytest · error · AttributeError

has no valid result

Error message

{self!r} has no valid result

What it means

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.

Solutions

  1. Guard with excinfo: if call.excinfo is None: result = call.result.
  2. In hooks, branch on call.when and call.excinfo before touching result.
  3. Use call.excinfo.getrepr(...) for failure reporting instead of result.

Example fix

// before
def pytest_runtest_makereport(item, call):
    result = call.result  # AttributeError on failure
// after
def pytest_runtest_makereport(item, call):
    if call.excinfo is not None:
        # failure path: use call.excinfo
        ...
        return
    result = call.result
Defensive patterns

Strategy: type-guard

Validate before calling

assert call.excinfo is None, f'cannot read result; call raised {call.excinfo!r}'
result = call.result

Type guard

def callinfo_has_result(call) -> bool:
    return getattr(call, 'excinfo', None) is None

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/cadbd03a070c65c9. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/runner.py:334

        *,
        _ispytest: bool = False,
    ) -> None:
        check_ispytest(_ispytest)
        self._result = result
        self.excinfo = excinfo
        self.start = start
        self.stop = stop
        self.duration = duration
        self.when = when

    @property
    def result(self) -> TResult:
        """The return value of the call, if it didn't raise.

        Can only be accessed if excinfo is None.
        """
        if self.excinfo is not None:
            raise AttributeError(f"{self!r} has no valid result")
        # The cast is safe because an exception wasn't raised, hence
        # _result has the expected function return type (which may be
        #  None, that's why a cast and not an assert).
        return cast(TResult, self._result)

    @classmethod
    def from_call(
        cls,
        func: Callable[[], TResult],
        when: Literal["collect", "setup", "call", "teardown"],
        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
    ) -> CallInfo[TResult]:
        """Call func, wrapping the result in a CallInfo.

        :param func:
            The function to call. Called without arguments.
        :type func: Callable[[], _pytest.runner.TResult]
        :param when:

View on GitHub (pinned to 0d6fbdeffa)