p-retry
Retry a promise-returning or async function with exponential backoff and fine-grained retry control.
Repository Health
Technical Analysis
p-retry wraps any promise-returning or async function so that a failed call is retried automatically instead of propagating the first rejection. It applies exponential backoff by default, and exposes three independent hooks — shouldConsumeRetry, onFailedAttempt, and shouldRetry — so callers can decide whether a failure counts against the retry budget, react to each attempt (logging, custom delays), and decide whether to keep retrying at all, all without touching the core loop.
Beyond the default export, the package ships a makeRetriable helper that wraps an existing function so every call to it is retried transparently, and an AbortError class for signalling that retries should stop immediately with no further callbacks invoked. Retry timing integrates with AbortSignal for cancellation and with a monotonic clock (performance.now()) for maxRetryTime, so retries stop cleanly under both explicit cancellation and elapsed-time budgets regardless of system clock changes.
What You Get
- A pRetry(input, options) function that retries a promise-returning function with configurable retries, factor, minTimeout, maxTimeout, and randomized jitter
- Three composable hooks — shouldConsumeRetry, onFailedAttempt, shouldRetry — for controlling retry budget consumption, side effects on failure, and retry eligibility independently
- An AbortError class for aborting all retries immediately from inside the retried function, with no further hooks invoked
- AbortSignal support for cancelling in-flight retries from outside the retry loop
- A maxRetryTime option measured with a monotonic clock so time budgets are immune to system clock adjustments
- A makeRetriable(function, options) helper that wraps any function so every call is automatically retried without repeating options at each call site
Common Use Cases
- Retrying flaky network requests (fetch calls) while treating 404s or other definitive failures as non-retryable via AbortError
- Wrapping a database or API client call so transient errors are retried with backoff but validation errors fail immediately
- Building a resilient job worker that retries a task a bounded number of times before giving up and reporting the last error
- Retrying an operation only up to a wall-clock time budget (maxRetryTime) rather than a fixed attempt count
Under The Hood
Architecture p-retry is a single-file module (index.js, under 300 lines) exporting a default async function pRetry, a named makeRetriable higher-order wrapper, and an AbortError class. The core loop in pRetry runs a bounded while loop calling the caller-supplied input(attemptNumber) function and routing any rejection through a dedicated onAttemptFailure helper that sequences shouldConsumeRetry, then onFailedAttempt, then shouldRetry — each of which can independently veto a retry, throw to abort immediately, or return a promise to defer. Delay math (calculateDelay) and remaining-time tracking (calculateRemainingTime, built on performance.now() for a monotonic clock) are extracted into small pure functions, and delayForRetry manages the actual setTimeout/AbortSignal race. There is no persistent internal state beyond function-local closures and no dependency injection; the load-bearing abstraction is the attempt loop’s error-classification order (AbortError, then non-network TypeError, then everything else), and changing that ordering would silently change retry semantics for every consumer that relies on TypeErrors from fetch-like APIs being retried only when they represent network failures.
Tech Stack A pure ESM package (type: module) targeting Node.js 22+, with a single runtime dependency, is-network-error, used to distinguish network TypeErrors from other TypeErrors for retry eligibility. Development tooling follows the author’s standard setup: ava as the test runner, xo (an opinionated ESLint config) for linting, tsd for testing the hand-written index.d.ts type definitions, execa for spawning subprocesses in tests, and delay as a test utility. There is no build step — index.js ships directly to npm as declared in the package’s files field. CI (GitHub Actions) runs the full npm test script (xo && ava && tsd) across Node.js 22 and 24.
Code Quality test.js contains extensive coverage (57 distinct test cases) exercising backoff/delay math, AbortError and AbortController interplay, the ordering and interaction of the three retry hooks, non-Error throws, and the removed forever option. This is paired with tsd-based type tests validating the public type surface and xo lint enforcement in CI. Error handling is explicit and typed throughout — invalid options throw descriptive TypeError instances via small dedicated validator functions rather than failing silently or coercing bad input. Naming is consistent and self-documenting across the module.
API Design The standout design choice is splitting failure handling into three independent, composable hooks — shouldConsumeRetry (does this failure count against the budget), onFailedAttempt (side effects like logging or a manual delay), and shouldRetry (should retrying continue at all) — rather than the single catch-all callback most retry libraries expose. Combined with first-class AbortSignal support woven directly into the delay logic (not bolted on after the fact), a monotonic-clock-based maxRetryTime immune to system clock skew, and the makeRetriable wrapper for turning any function into a self-retrying one without re-specifying options per call, the public API stays small while covering cases most retry utilities require workarounds for.
Used by 10 apps in this directory
Dittofeed
Marketing · Automation
Open-source omni-channel customer engagement platform for automating transactional and marketing messages via email, SMS, WhatsApp, Slack, and mobile push.
GraphQL Hive
Developer Tools · Devops · Monitoring
Open-source GraphQL schema registry and observability platform with breaking change detection, federation support, and CI/CD integration for teams of any size.
Kibana
Analytics · Monitoring
Your open source window into the Elastic Stack — query, visualize, and act on data stored in Elasticsearch with real-time dashboards, AI-assisted search, and automated alerting.
Logto
Authentication
Open-source auth infrastructure for SaaS and AI apps with OIDC, SAML, and RBAC
Mastra Code
AI Code Assistants
"A coding agent that never compacts" — a terminal-based AI coding agent built on the Mastra framework, with Observational Memory instead of context compaction, multi-model support, and OAuth login for Claude Max or ChatGPT Plus.
NemoClaw
AI Agents · AI Development
Run AI coding agents like OpenClaw and Hermes securely inside NVIDIA OpenShell sandboxes with a hardened blueprint, routed inference, and lifecycle management through a single CLI.
Rivet
AI Agents · Developer Tools
Stateful actors as a primitive for AI agents, real-time collaboration, and durable execution — with in-memory state, WebSockets, queues, and scheduling built in.
Trigger.dev
Automation · AI Development · Developer Tools
Build and deploy fully-managed AI agents and background jobs in TypeScript — with no timeouts, durable retries, real-time observability, and elastic scaling built in.
Webiny JS
Ecommerce · Blogging · CMS
Open-source, self-hosted CMS on AWS serverless — a TypeScript framework you extend with code, not a product you configure through a UI.