Copper.Retry 0.0.5

dotnet add package Copper.Retry --version 0.0.5
                    
NuGet\Install-Package Copper.Retry -Version 0.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Copper.Retry" Version="0.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Copper.Retry" Version="0.0.5" />
                    
Directory.Packages.props
<PackageReference Include="Copper.Retry" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Copper.Retry --version 0.0.5
                    
#r "nuget: Copper.Retry, 0.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Copper.Retry@0.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Copper.Retry&version=0.0.5
                    
Install as a Cake Addin
#tool nuget:?package=Copper.Retry&version=0.0.5
                    
Install as a Cake Tool

Copper.Retry

🔁 A small and focused retry library for .NET.

Copper.Retry provides Polly-style retry policies without the additional resilience features you may not need.

Define which exceptions should trigger a retry, configure the delays between attempts, and execute your operation through the policy.

Binaries

Package Version Description
Copper.Retry NuGet Version Core retry library

Quick start

Define a retry policy:

static readonly RetryPolicy RetryPolicy = RetryPolicy
    .Handle<IOException>()
    .WaitAndRetry([
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(1)
    ]);

Then execute an operation through the policy:

await RetryPolicy.ExecuteAsync(
    async token => await DoSomethingAsync(token),
    cancellationToken
);

If DoSomethingAsync throws an IOException, the policy waits for the configured delay and tries again.

If the operation succeeds, execution returns immediately.

If all retries are exhausted, the last exception is propagated to the caller.

Retry policies

A retry policy defines two things:

  1. What should be retried
  2. When it should be retried

For example:

static readonly RetryPolicy RetryPolicy = RetryPolicy
    .Handle<HttpRequestException>()
    .WaitAndRetry([
        TimeSpan.FromMilliseconds(100),
        TimeSpan.FromMilliseconds(500),
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5)
    ]);

The operation is attempted immediately.

If it fails with a handled exception, the first delay is applied before the second attempt:

Attempt 1
   ↓ failure
wait 100 ms

Attempt 2
   ↓ failure
wait 500 ms

Attempt 3
   ↓ failure
wait 1 second

Attempt 4
   ↓ failure
wait 5 seconds

Attempt 5

The number of configured delays therefore determines the number of retries, not the total number of attempts.

Four delays means up to five attempts.

Retrying forever

Use WaitAndRetryForever to keep retrying until the operation succeeds, waiting for the same delay before each retry:

var policy = RetryPolicy
    .Handle<HttpRequestException>()
    .WaitAndRetryForever(TimeSpan.FromSeconds(5));

The execution then only ends when the operation succeeds, throws an exception not handled by the policy, or is cancelled – so be sure to pass a CancellationToken to ExecuteAsync to be able to stop it.

Handling exceptions

Use Handle<TException>() to specify which exceptions should trigger a retry:

var policy = RetryPolicy
    .Handle<HttpRequestException>()
    .WaitAndRetry([
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5)
    ]);

Pass a predicate to Handle<TException>() to apply arbitrary logic when checking whether to handle the exception:

var policy = RetryPolicy
    .Handle<HttpRequestException>(e => e.StatusCode != 400) //< bad requests stay bad
    .WaitAndRetry([
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5)
    ]);

Call Handle<TException>() multiple times to add more filters:

var policy = RetryPolicy
    .Handle<HttpRequestException>(e => e.StatusCode != 400) //< bad requests stay bad
    .Handle<IOException>()
    .WaitAndRetry([
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5)
    ]);

Exceptions not handled by the policy are propagated immediately without retrying.

Retry callbacks

Use OnRetry to get notified each time an operation is about to be retried, e.g. to log transient errors:

var policy = RetryPolicy
    .Handle<HttpRequestException>()
    .OnRetry((exception, delay) => logger.LogWarning(exception, "Request failed - retrying in {Delay}", delay))
    .WaitAndRetry([
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5)
    ]);

The callback receives the exception that caused the retry and the delay that will be waited before the next attempt.

Call OnRetry multiple times to add more callbacks. They are invoked in the order they were added.

Callbacks are only invoked when a retry will actually happen, so they are not invoked for the last exception, which is propagated to the caller.

Executing operations

Execute asynchronous operations using ExecuteAsync:

await policy.ExecuteAsync(await SendRequestAsync(token), cancellationToken);

Operations returning a value can be executed in the same way:

var result = await policy.ExecuteAsync(
    async cancellationToken =>
    {
        return await GetSomethingAsync(cancellationToken);
    },
    cancellationToken
);

Cancellation

ExecuteAsync accepts a CancellationToken:

await policy.ExecuteAsync(
    async cancellationToken =>
    {
        await DoSomethingAsync(cancellationToken);
    },
    cancellationToken
);

Cancellation stops execution and prevents further retry attempts.

The cancellation token is also used while waiting between attempts, allowing pending retry delays to be cancelled immediately.

Reusing policies

Retry policies are intended to be created once and reused:

static readonly RetryPolicy ApiRetryPolicy = RetryPolicy
    .Handle<HttpRequestException>()
    .WaitAndRetry([
        TimeSpan.FromMilliseconds(250),
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5)
    ]);

The same policy can then be used by multiple operations:

await ApiRetryPolicy.ExecuteAsync(
    token => CallApiAsync(token),
    cancellationToken
);

Policies are safe to reuse across executions.

Why Copper.Retry?

Libraries such as Polly provide a comprehensive set of resilience strategies, including retries, circuit breakers, timeouts, rate limiting, hedging, and fallback policies.

Copper.Retry deliberately has a much smaller scope.

It is for applications that need retries — and just retries.

The basic model is simple:

Try. If it fails with an exception you care about, wait a while and try again.

License

Copper.Retry is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.5 59 10/9/2026
0.0.4 54 10/8/2026
0.0.3 62 10/8/2026
0.0.2 52 10/7/2026
0.0.1 56 10/7/2026