python-backoff

raw JSON →
2.3.1 verified Tue May 12 auth: no python install: verified quickstart: verified

python-backoff is a Python library providing function decorators for configurable backoff and retry strategies. It allows you to wrap functions to automatically retry their execution until a specified condition is met, such as handling transient failures from unreliable network resources or external APIs. The library supports both synchronous and asynchronous (asyncio) code, offering various wait generators like exponential, fibonacci, and constant backoff. It is actively maintained, with recent releases in late 2025, and the current version is 2.3.1.

pip install backoff
error ModuleNotFoundError: No module named 'backoff'
cause The 'backoff' library is not installed in the Python environment, or there is an issue with the Python path.
fix
Install the library using pip: pip install backoff. If using Python 3.7 and encountering 'importlib.metadata' issues with backoff v2.1.1, consider upgrading Python or installing an older, compatible version of backoff (e.g., pip install backoff==2.1.0).
error TypeError: catching classes that do not inherit from BaseException is not allowed
cause The `backoff` decorator's `exception` parameter was provided with an object that is not a proper Python exception class, such as an instance of an exception or a non-exception type.
fix
Ensure that only actual exception classes (e.g., ValueError, requests.exceptions.RequestException, Exception) are passed to the exception parameter of @backoff.on_exception.
error @backoff.on_exception(max_tries=5) def my_function(): pass
cause The `@backoff.on_exception` decorator requires at least a `wait_gen` (a wait generator like `backoff.expo` or `backoff.constant`) and an `exception` type to be specified to define the retry strategy and which errors to catch. Omitting these will result in the decorator not functioning as intended or retrying on all exceptions without a specific backoff strategy.
fix
Provide a wait generator (e.g., backoff.expo) and the specific exception type(s) to retry on. For example: @backoff.on_exception(backoff.expo, ValueError, max_tries=5).
breaking Python 3.7 support was dropped in `backoff` version 2.3.0. Users on Python 3.7 attempting to upgrade to 2.3.0 or later will encounter compatibility issues.
fix Upgrade your Python environment to 3.8 or newer before upgrading `backoff`.
breaking In `backoff` v2.3.1, the `target` key in the `details` dictionary passed to event handlers (like `on_backoff`, `on_giveup`) changed its type from a string representation to an actual function reference.
fix If your event handlers inspect `details['target']` and expect a string, update your logic to handle a function object instead. For example, use `details['target'].__name__` to get the function name.
gotcha The `@backoff.on_exception` decorator requires the decorated function to raise the specified exception (or a subclass) to trigger a retry. If the function catches the exception internally with a bare `except:` clause and does not re-raise it, `backoff` will not detect the failure and will not retry.
fix Ensure that the exceptions you want `backoff` to handle are propagated out of the decorated function. Avoid broad `except:` clauses, or explicitly `raise` the exception after handling it internally if retries are still desired.
gotcha When using `@backoff.on_exception`, it is crucial to provide both a wait generator (e.g., `backoff.expo`, `backoff.constant`) and at least one exception type to retry on. Failing to provide these parameters makes the decorator ineffective.
fix Always specify the wait strategy and the exceptions. Example: `@backoff.on_exception(backoff.expo, SomeError, max_tries=5)`.
python os / libc status wheel install import disk
3.10 alpine (musl) - - - -
3.10 alpine (musl) - - 0.10s 17.9M
3.10 slim (glibc) - - - -
3.10 slim (glibc) - - 0.07s 18M
3.11 alpine (musl) - - - -
3.11 alpine (musl) - - 0.19s 19.7M
3.11 slim (glibc) - - - -
3.11 slim (glibc) - - 0.14s 20M
3.12 alpine (musl) - - - -
3.12 alpine (musl) - - 0.41s 11.6M
3.12 slim (glibc) - - - -
3.12 slim (glibc) - - 0.34s 12M
3.13 alpine (musl) - - - -
3.13 alpine (musl) - - 0.39s 11.2M
3.13 slim (glibc) - - - -
3.13 slim (glibc) - - 0.36s 12M
3.9 alpine (musl) - - - -
3.9 alpine (musl) - - 0.09s 17.4M
3.9 slim (glibc) - - - -
3.9 slim (glibc) - - 0.08s 18M

This quickstart demonstrates how to use the `@backoff.on_exception` decorator to automatically retry a function that might fail intermittently. It configures exponential backoff with jitter, sets a maximum number of tries and total elapsed time, and includes `on_backoff` and `on_giveup` handlers for logging retry attempts. The example simulates a flaky API that raises exceptions for the first few calls, eventually succeeding or giving up.

import backoff
import random
import time

# Simulate a flaky external API call
CALL_COUNT = 0
MAX_FAILURES = 3

@backoff.on_exception(backoff.expo,  # Exponential backoff strategy
                      (ValueError, IOError), # Retry on these exceptions
                      max_tries=5,         # Max attempts
                      max_time=30,         # Max total time in seconds
                      jitter=backoff.full_jitter, # Add randomness to delays
                      on_backoff=lambda details: print(f"Backing off {details['wait']:.2f}s for {details['target'].__name__} after {details['tries']} tries, due to {details['exception'].__class__.__name__}"),
                      on_giveup=lambda details: print(f"Giving up on {details['target'].__name__} after {details['tries']} tries, last exception {details['exception'].__class__.__name__}"))
def call_flaky_api():
    global CALL_COUNT
    CALL_COUNT += 1
    print(f"Attempt {CALL_COUNT}: Calling flaky API...")
    if CALL_COUNT <= MAX_FAILURES:
        if random.random() < 0.8: # 80% chance to raise ValueError initially
            raise ValueError("API returned invalid data")
        else:
            raise IOError("Network connection lost") # Simulate another failure
    print(f"Attempt {CALL_COUNT}: API call successful!")
    return "Data from API"


try:
    result = call_flaky_api()
    print(f"Final result: {result}")
except (ValueError, IOError) as e:
    print(f"Operation failed after multiple retries: {e}")

# Reset call count for a new demonstration
CALL_COUNT = 0
print("\n--- Demonstrating with higher success chance after initial failures ---")
MAX_FAILURES = 1 # Will likely succeed on the 2nd attempt
try:
    result = call_flaky_api()
    print(f"Final result: {result}")
except (ValueError, IOError) as e:
    print(f"Operation failed after multiple retries: {e}")