Skip to content

retry_if_exception_cause_type spins forever when the exception cause chain contains a cycle (e.g. raise e from e) #658

Closed

Description

@earfman

When the exception cause chain contains a cycle, retry_if_exception_cause_type never returns — the predicate spins at 100% CPU and the retry can neither proceed nor stop.

The scan in tenacity/retry.py walks __cause__ until it reaches None:

exc = retry_state.outcome.exception()
while exc is not None:
    if isinstance(exc.__cause__, self.exception_cause_types):
        return True
    exc = exc.__cause__

A cyclic chain never reaches None. Because the spin happens inside the retry predicate, stop_after_attempt / stop_after_delay never get the chance to end the retry — stop conditions are only consulted after the predicate returns. The docstring's premise (“until finding an exception in the chain that has no __cause__”) doesn't hold for cyclic chains.

The simplest cycle is ordinary, legal Python:

from tenacity import Retrying, retry_if_exception_cause_type, stop_after_attempt

def boom():
    try:
        raise ValueError("inner")
    except ValueError as e:
        raise e from e   # self-referential __cause__

r = Retrying(
    retry=retry_if_exception_cause_type(KeyError),
    stop=stop_after_attempt(2),
    reraise=True,
)
r(boom)   # never returns; 100% CPU

Two-node cycles (a.__cause__ = b; b.__cause__ = a, then raise a) hang the same way. For precedent: the stdlib traceback module explicitly guards against cause/context cycles when walking exception chains, so cyclic chains are treated as a real case in the ecosystem.

Reproduced on current master (c650fb4, v9.1.4); the loop is present in every release that ships this predicate.

Related note: the proposed retry_unless_exception_cause_type feature (#625 / #626) walks the cause chain the same way and would inherit this hang unless the walk is cycle-safe.

Fix is two lines (track seen exception ids; stop when the chain repeats). PR ready with a regression test covering both cycle shapes.

Activity

  1. added a commit that references this issue on Jul 29, 2026
    b3c5a9f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions