pytest-dev/pytest · error · AttributeError
{self!r} has no valid result
Error message
{self!r} has no valid result What it means
Raised by the CallInfo.result property when a plugin or hook accesses .result on a CallInfo whose underlying call actually raised an exception (excinfo is not None). CallInfo is pytest's internal wrapper for each test phase (setup/call/teardown) created via CallInfo.from_call; .result is only meaningful when the call succeeded. Accessing it after a failure is a contract violation on the plugin author's side.
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 98b357f69e)
Solutions
- Guard the access: read call.result only inside `if call.excinfo is None:` blocks.
- If you only need the exception, use call.excinfo directly instead of call.result.
- When wrapping via from_call, pass the reraise= argument so expected exceptions propagate instead of being stored as excinfo.
- Update to a newer version of the offending plugin; this is almost always a plugin bug, not a test bug.
Example fix
// before
from _pytest.runner import CallInfo
def my_hook(call: CallInfo):
value = call.result # raises AttributeError on failure
// after
from _pytest.runner import CallInfo
def my_hook(call: CallInfo):
if call.excinfo is not None:
return # or handle call.excinfo
value = call.result Defensive patterns
Strategy: type-guard
Validate before calling
from _pytest.runner import CallInfo
def safe_result(call: CallInfo):
if call.excinfo is not None:
raise call.excinfo.value
return call.result Type guard
from typing import TypeGuard
from _pytest.runner import CallInfo
def call_succeeded(call: CallInfo) -> TypeGuard[CallInfo]:
return call.excinfo is None Try / catch
try:
value = call.result
except AttributeError:
# call raised; inspect call.excinfo instead
assert call.excinfo is not None
handle(call.excinfo.value) Prevention
- Always branch on call.excinfo before touching call.result.
- Prefer the reraise= argument to from_call so expected exceptions propagate rather than becoming excinfo.
- Treat AttributeError from .result as a programming error in your hook, not a runtime condition.
When it happens
Trigger: A pytest hook or plugin calls CallInfo.from_call(...) and then unconditionally reads call.result without first checking call.excinfo. Reproduces whenever the wrapped func raises during the from_call invocation, because excinfo gets set and the property then refuses to hand back a (nonexistent) return value.
Common situations: Third-party plugins that wrap runtest protocol hooks and forget the excinfo check; test frameworks building on top of pytest that assume the call always succeeds; migrating plugins across pytest versions where the API became stricter about the result/excinfo invariant.
Related errors
- config or parent must be provided
- session or parent must be provided
- nodeid or parent must be provided
- config is not a valid argument for from_parent
- session is not a valid argument for from_parent
AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04).
Data as JSON: /data/errors/cadbd03a070c65c9.json.
Report an issue: GitHub.