Custom retry policy

Pluggable retry decisions via retryPolicy

The default retry strategy is retryDelay x 2attempt. When you need smarter behavior - honoring Retry-After on 429/503, classifying permanent vs transient errors, jitter to avoid thundering herd, or capping at a wall-clock budget - pass a retryPolicy function that returns the desired delay (or false to abort).

Function signature
retryPolicy: function(attempt, error, ctx) {
 // attempt = 1-based count of the NEXT try (so 1 on first failure) // error = the Error that triggered the retry consideration // ctx = { task, kind: 'task'|'chunk'|'control', index? } // Return value: // number = milliseconds to wait before the next attempt // false = abort immediately (no retry) // true = use the default exponential backoff
}
Recipe: honor Retry-After on 429/503
retryPolicy: function(attempt, err) {
 // Server hint takes precedence over our backoff curve. if (err.retryAfter) return Math.min(err.retryAfter * 1000, 60000);
 return true; // fall back to default
}
Recipe: never retry a permanent error
retryPolicy: function(attempt, err) {
 // 401, 403, 404, 410, 422 are unrecoverable; don't waste time retrying. if (/HTTP (401|403|404|410|422)/.test(err.message)) return false;
 return true;
}
Recipe: cap by wall-clock budget
var uploadDeadline = Date.now() + 5 * 60 * 1000; // 5-minute hard limit 
retryPolicy: function(attempt, err) {
 if (Date.now() > uploadDeadline) return false;
 return true;
}
Recipe: jittered exponential
retryPolicy: function(attempt) {
 var base = 1000 * Math.pow(2, attempt - 1);
 return base + Math.random() * base * 0.5; // +/-25% jitter
}
Safety
  • Returned delays are capped at 10 minutes internally to prevent runaway sleeps.
  • The total number of attempts still respects options.retries.
  • If the policy throws, the default exponential backoff is used (your retries don't break upload).