Works with any backend npm install Zero dependencies

Pluggable retry decisions via retryPolicy

The default retry strategy is retryDelay x 2attempt. Pass a retryPolicy function for smarter behaviour: honouring Retry-After on 429/503, classifying permanent vs transient errors, jitter to avoid thundering herd, or capping at a wall-clock budget. Return a delay in ms, false to abort, or true to fall back to the default.

Function signature: (attempt, error, ctx) => number | false | true where attempt is the 1-based next try, error is the failure, and ctx is { task, kind: 'task'|'chunk'|'control', index? }.
Recipe library
// 1. Honor Retry-After on 429/503
retryPolicy: (attempt, err) => err.retryAfter ? err.retryAfter * 1000 : true

// 2. Never retry permanent errors
retryPolicy: (attempt, err) => /HTTP (401|403|404|410|422)/.test(err.message) ? false : true

// 3. Wall-clock budget (5 minutes)
const deadline = Date.now() + 5 * 60_000;
retryPolicy: () => Date.now() > deadline ? false : true

// 4. Jittered exponential (avoid thundering herd)
retryPolicy: (attempt) => {
 const base = 1000 * Math.pow(2, attempt - 1);
 return base + Math.random() * base * 0.5;
}

// 5. Per-error-class strategy
retryPolicy: (attempt, err, ctx) => {
 if (ctx.kind === 'chunk' && /timeout/i.test(err.message)) return 5000; // give chunks more time
 if (err.message.startsWith('HTTP 5')) return 1000 * attempt; // linear for 5xx
 return true; // exponential default
}
Where it's invoked

Called whenever a task or chunk fails and a retry is being considered. Triggered from:

  • Task-level retry - full upload retried after a single-strategy failure
  • Chunk-level retry - applies to chunked / s3 / azure / gcs strategies on individual chunk PUTs
  • Control-plane retry - applies to s3 sign / azure create / gcs initiate / tus session-create network failures
Safety guarantees
  • Returned delays are capped at 10 minutes internally - your runaway sleep can't strand a queue.
  • The total number of attempts still respects options.retries.
  • If your policy throws, the default exponential backoff applies - broken policies don't break uploads.
  • Sleeps respect cancel / pause - aborted tasks don't accumulate dead promises.