You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
--only-rerun / only_rerun matches against the outermost exception only. When a transient error is wrapped by another exception via raise ... from error, the pattern can no longer match it, so the test is not retried even though the underlying error is exactly the kind users configure only_rerun for.
Current behaviour
_try_match_error builds the string to match from excinfo alone (src/pytest_rerunfailures.py, master):
excinfo.type and excinfo.value are the outermost exception. Neither __cause__ nor __context__ is consulted, so both the regex form and the exception-type form miss a wrapped error.
Reproduction
importpytestdeftest_wrapped():
try:
raiseMemoryError("out of memory")
exceptMemoryErroraserror:
raiseRuntimeError("something failed, see the exception above") fromerror
$ pytest test_wrapped.py --reruns 2 --only-rerun MemoryError -rR
1 failed in 0.01s # not rerun
We hit this in the TRL CI, where the suite runs under pytest-xdist on a shared GPU and --only-rerun includes OutOfMemoryError to absorb memory pressure between workers.
When the OOM is raised inside torch.testing.assert_close, torch.testing wraps any exception it does not expect:
raiseRuntimeError(
f"Comparing\n\n"f"{pair}\n\n"f"resulted in the unexpected exception above. "
...
) fromerror
The failure then surfaces as RuntimeError: Comparing ..., with torch.OutOfMemoryError demoted to __cause__. The wrapper message does not carry the original error text, so no only_rerun value can match it: neither OutOfMemoryError (wrong outer type) nor CUDA out of memory (absent from the outer message).
The only workaround available to us is matching the wrapper text itself, which we rejected: torch.testing emits that message for any unexpected exception during a comparison, so it would also retry genuine defects. A deterministic bug would still fail all attempts, but a nondeterministic one would be silently retried into green, which is precisely what a narrow only_rerun is meant to prevent.
Proposed behaviour
Walk the exception chain and match if any linked exception matches, something like:
__cause__ is the unambiguous signal (explicit raise ... from). Following __context__ as well also covers exceptions raised inside an except block without from, which is the more common accidental wrapping. If following __context__ is considered too broad, restricting to __cause__ alone would already fix the case above and could be worth gating behind an option.
Happy to open a PR if the approach looks right.
Environment
pytest-rerunfailures 16.6 (behaviour unchanged on master), also reproduced on 15.1
Summary
--only-rerun/only_rerunmatches against the outermost exception only. When a transient error is wrapped by another exception viaraise ... from error, the pattern can no longer match it, so the test is not retried even though the underlying error is exactly the kind users configureonly_rerunfor.Current behaviour
_try_match_errorbuilds the string to match fromexcinfoalone (src/pytest_rerunfailures.py, master):excinfo.typeandexcinfo.valueare the outermost exception. Neither__cause__nor__context__is consulted, so both the regex form and the exception-type form miss a wrapped error.Reproduction
Removing the wrapper makes it retry as expected:
Why this matters in practice
We hit this in the TRL CI, where the suite runs under
pytest-xdiston a shared GPU and--only-rerunincludesOutOfMemoryErrorto absorb memory pressure between workers.When the OOM is raised inside
torch.testing.assert_close,torch.testingwraps any exception it does not expect:The failure then surfaces as
RuntimeError: Comparing ..., withtorch.OutOfMemoryErrordemoted to__cause__. The wrapper message does not carry the original error text, so noonly_rerunvalue can match it: neitherOutOfMemoryError(wrong outer type) norCUDA out of memory(absent from the outer message).The only workaround available to us is matching the wrapper text itself, which we rejected:
torch.testingemits that message for any unexpected exception during a comparison, so it would also retry genuine defects. A deterministic bug would still fail all attempts, but a nondeterministic one would be silently retried into green, which is precisely what a narrowonly_rerunis meant to prevent.Proposed behaviour
Walk the exception chain and match if any linked exception matches, something like:
With that, the existing
OutOfMemoryErrorpattern matches the wrapped case on its own, with no ambiguity and no wrapper-text heuristic.Two notes on scope:
conditioncallback, whose implementation (Allow to pass a callable condition to theflakymarker #299) was reverted in Makepytest-xdisthappy again #304 forpytest-xdistincompatibility. Chain walking needs no user callback and no new serialization: it reads type names andstr()of already-available exceptions, and the decision is still taken worker-side inpytest_runtest_makereport, exactly where it is taken today.__cause__is the unambiguous signal (explicitraise ... from). Following__context__as well also covers exceptions raised inside anexceptblock withoutfrom, which is the more common accidental wrapping. If following__context__is considered too broad, restricting to__cause__alone would already fix the case above and could be worth gating behind an option.Happy to open a PR if the approach looks right.
Environment
pytest-rerunfailures16.6 (behaviour unchanged on master), also reproduced on 15.1