tenacity
Retrying library for Python — provides @retry decorator and Retrying context manager with configurable retry strategies, wait policies, stop conditions, and async support. tenacity features: @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=60), retry=retry_if_exception_type(RequestException)), stop strategies (stop_after_attempt, stop_after_delay, stop_never), wait strategies (wait_fixed, wait_exponential, wait_random, wait_chain, wait_none), retry conditions (retry_if_exception_type, retry_if_result, retry_if_not_result), before/after/before_sleep callbacks, reraise=True option, AsyncRetrying for async, and statistics tracking.
Score Breakdown
⚙ Agent Friendliness
🔒 Security
Retry utility library. Retrying POST/mutating operations may cause duplicate effects — ensure idempotency before using retry. Exponential backoff without max can cause indefinite delays — always set max. Retry amplifies load on failing services — implement circuit breaker for sustained failures.
⚡ Reliability
Best When
Retrying transient failures in API calls, database operations, and network requests — tenacity is the most feature-complete retry library with excellent async support and composable strategies.
Avoid When
Circuit breaker patterns (use pybreaker), rate limiting compliance (use ratelimit), or when backoff library's simpler API suffices.
Use Cases
- • Agent API retry — from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type; @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=30), retry=retry_if_exception_type(requests.RequestException)); def fetch_api(url): return requests.get(url).json() — exponential backoff; agent retries transient failures with backoff
- • Agent retry with logging — @retry(stop=stop_after_attempt(5), wait=wait_exponential(min=1, max=10), before_sleep=before_sleep_log(logger, logging.WARNING)); def call_service(): return service.call() — callback; agent logs each retry attempt; before_sleep= called before sleeping; after= called after each attempt; statistics tracked in function.retry.statistics
- • Agent async retry — from tenacity import AsyncRetrying, stop_after_attempt, wait_fixed; async with AsyncRetrying(stop=stop_after_attempt(3), wait=wait_fixed(2)) as r: async for attempt in r: with attempt: result = await async_operation() — async context; agent retries async operations; AsyncRetrying context manager for fine-grained control
- • Agent retry on result — @retry(retry=retry_if_result(lambda r: r.get('status') == 'pending'), stop=stop_after_attempt(10), wait=wait_fixed(5)); def poll_job(job_id): return api.get_job_status(job_id) — result-based retry; agent polls until operation completes or max retries exceeded; retry_if_result checks return value
- • Agent conditional retry — @retry(stop=stop_after_attempt(3), retry=retry_if_exception_type((ConnectionError, TimeoutError)), reraise=True); def connect(): return create_connection() — selective retry; agent retries only specific exception types; reraise=True re-raises last exception after all retries exhausted (instead of tenacity.RetryError)
Not For
- • Circuit breaker pattern — tenacity is retry-only; for circuit breaker (stop retrying after sustained failures) use pybreaker or custom logic
- • Rate limiting — tenacity retries on failure; for rate limit compliance use ratelimit or per-request delay
- • Async retry with complex flow — for complex async retry with cancel support use anyio or custom async retry logic
Interface
Authentication
No auth — retry utility library.
Pricing
tenacity is Apache 2.0 licensed. Free for all use.
Agent Metadata
Known Gotchas
- ⚠ reraise=False by default — @retry without reraise=True raises tenacity.RetryError after all attempts; original exception wrapped inside RetryError.last_attempt.exception(); agent code: use reraise=True to get original exception type in except clause; or catch tenacity.RetryError and inspect .last_attempt
- ⚠ wait_exponential defaults can be surprising — wait_exponential() default: multiplier=1, min=0, max=inf; first retry: 0-1s; grows unbounded; agent code: always specify max: wait_exponential(multiplier=1, min=1, max=60); add wait_random_min/max for jitter: wait_exponential() + wait_random(min=0, max=2)
- ⚠ @retry on class methods requires self — @retry cannot be applied as class method decorator directly when it accesses instance state; tenacity.retry wraps function not method; agent code: @retry works fine on class methods for pure retry logic; for instance-level retry configuration: use tenacity.Retrying context manager inside method body
- ⚠ stop_after_delay measures wall clock time — stop_after_delay(30) stops after 30 seconds total including all wait times; if wait=wait_fixed(10) and stop_after_delay(30): maximum 3 attempts; agent code: combine stop strategies: stop=(stop_after_attempt(5) | stop_after_delay(60)); | operator means 'stop if either condition met'
- ⚠ Async functions need async retry — @retry on regular function works; @retry on async function with tenacity 8.x does work (tenacity detects async); AsyncRetrying context manager for manual control: async with AsyncRetrying(...) as r: async for attempt in r: with attempt: result = await coro(); for explicit async control
- ⚠ Statistics tracking requires accessing after call — fn.retry.statistics returns dict only after function called; agent code: call fn(); then log fn.retry.statistics['attempt_number']; or use after= callback: after=after_log(logger, logging.DEBUG); statistics reset on each top-level call; shared across concurrent calls
Alternatives
Full Evaluation Report
Comprehensive deep-dive: security analysis, reliability audit, agent experience review, cost modeling, competitive positioning, and improvement roadmap for tenacity.
AI-powered analysis · PDF + markdown · Delivered within 30 minutes
Package Brief
Quick verdict, integration guide, cost projections, gotchas with workarounds, and alternatives comparison.
Delivered within 10 minutes
Score Monitoring
Get alerted when this package's AF, security, or reliability scores change significantly. Stay ahead of regressions.
Continuous monitoring
Scores are editorial opinions as of 2026-03-06.