retry-go
A lightweight Go library for retrying fallible functions with configurable backoff, jitter, and per-error attempt limits.
Repository Health
Technical Analysis
retry-go gives Go developers a simple, composable way to retry an operation that might fail transiently — an HTTP call, a database write, a queue publish — without hand-rolling a loop, a timer, and error-accumulation logic every time. The core API is two functions, Do for functions returning only an error and DoWithData for generic functions that also return a typed value, both driven by a small set of functional options.
Underneath, retry-go tracks every failed attempt and returns them as a combined Error (a []error with Is/As/Unwrap support), so callers can inspect the full failure history or just the last error via LastErrorOnly. Retry behavior is fully pluggable: attempt counts (including unlimited, or per-specific-error caps via AttemptsForError), delay strategies (fixed, exponential backoff, random jitter, or combinations), context cancellation, and a custom RetryIf predicate for deciding which errors are worth retrying at all. Wrapping an error in retry.Unrecoverable short-circuits retries immediately, which is a common pattern for distinguishing “retryable” from “fatal” failures in application code.
The library has no runtime dependencies beyond the Go standard library (only testify is pulled in for tests), making it a safe, low-friction addition to any Go project that talks to a flaky external system.
What You Get
Do(fn, opts...)for retrying a plainfunc() error, and genericDoWithData(fn, opts...)for functions that also return a typed value on success- Configurable delay strategies —
FixedDelay,BackOffDelay(exponential),RandomDelay(jitter),FullJitterBackoffDelay, andCombineDelayto compose several together - Attempt control via
Attempts(n),UntilSucceeded()for unbounded retry, andAttemptsForError(n, err)to cap retries for one specific error independently of the overall limit Unrecoverable(err)/IsRecoverable(err)to mark an error as non-retryable and stop the loop immediately, plus a customRetryIfpredicate for arbitrary retry-worthiness logic- Context support via
Context(ctx)so retries respect cancellation and deadlines, withWrapContextErrorWithLastErrorto preserve the last function error alongside the context error - A combined
Errortype ([]error) implementingIs,As, andUnwrapfor standard-library-compatible error inspection, orLastErrorOnly(true)to surface just the final failure
Common Use Cases
- Retrying outbound HTTP requests to a flaky or rate-limited third-party API with exponential backoff and jitter to avoid thundering-herd retries
- Retrying database writes or queue publishes that occasionally fail on transient connection errors, while treating validation errors as unrecoverable via
Unrecoverable - Wrapping health-check or readiness-probe polling loops with
UntilSucceeded()bounded by aContextdeadline - Building resilient CLI tools or background workers that need bounded retry budgets per error type via
AttemptsForError - Testing retry behavior deterministically by swapping in a custom
TimerviaWithTimerinstead of waiting on real time.After delays
Under The Hood
Architecture
The library is intentionally small and centers on two exported entry points in retry.go: Do, which adapts a plain func() error into the generic path, and DoWithData[T], which holds the actual retry loop. Each call builds a *Config from newDefaultRetryConfig() and applies the caller’s Option functions (from options.go) before entering one of two loops — an unbounded loop when Attempts(0)/UntilSucceeded() is set, or a bounded for loop with a shouldRetry label otherwise. Delay computation, error accumulation into the Error slice type, and context-cancellation handling are all inline in this loop rather than split into separate collaborators, keeping the control flow traceable in one place. Swapping BackOffDelay for FullJitterBackoffDelay or composing multiples via CombineDelay doesn’t touch the loop at all, since every delay strategy is just a DelayTypeFunc(n, err, config) time.Duration — the loop only ever calls one function pointer through config.delayType.
Tech Stack
Written in modern Go (go 1.20 in go.mod) and using generics (RetryableFuncWithData[T any], DoWithData[T any]) to support typed return values without reflection or interface{} casts. The only non-test dependency is the standard library; testify is a test-only dependency. There’s no build tooling beyond a Makefile wrapping go test, go vet, and golangci-lint, and no external services or runtime configuration — it’s a pure, importable API surface.
Code Quality
The repo has an extensive test suite (retry_test.go, last_error_test.go, plus scenario tests under examples/ for HTTP GETs, delay-based-on-error, and error-history handling) using testify/assert for expressive assertions, and CI runs the matrix across multiple Go versions and operating systems with golangci-lint as a separate required job. Error handling is explicit and typed throughout — the custom Error type implements Is/As/Unwrap to integrate cleanly with the standard errors package rather than relying on string matching, and the unrecoverableError wrapper type gives callers a structured way to opt out of retries.
What Makes It Unique
What distinguishes retry-go from more generic backoff libraries is its combination of per-error attempt budgets (AttemptsForError) alongside the overall attempt limit, its accumulation of the full multi-attempt error history into a single inspectable Error value (rather than only the last error), and first-class generics support for functions that return data on success — most comparable Go retry libraries predate generics and only support the plain func() error signature.
Used by 5 apps in this directory
agentgateway
AI Development · Developer Tools
An open source AI-native proxy that secures, observes, and governs agent-to-LLM, agent-to-tool, and agent-to-agent communication through MCP, A2A, and unified LLM routing.
authentik
Authentication · Security
The self-hosted Identity Provider that replaces Okta, Auth0, and Entra ID with a unified SSO platform supporting SAML, OAuth2/OIDC, LDAP, RADIUS, and WebAuthn.
OpenMeter
Invoicing Finance · Developer Tools
Open-source metering and billing engine for AI, agentic, and DevTool monetization — ingest usage events in real time and turn them into accurate invoices automatically.
Ory Kratos
Authentication
API-first identity and user management that handles login, registration, MFA, and recovery so your application never has to.
tau
Devops
Open-source, Git-native platform-as-a-service for building, deploying, and scaling fullstack apps on your own infrastructure with no DevOps required.