onRetry
Runs each time the SDK schedules a retry for a single-operation call, with the upcoming attempt, its delay, and the triggering error.
A constructor hook that runs each time the SDK schedules a retry for a single-operation call, with the upcoming attempt, the delayMs before it, and the error that triggered it. It never fires on the first attempt, for non-retryable errors, or for stream uploads (which aren’t retried). The array forms’ own per-item calls never fire it either — they settle as a single aggregated onAction event for the whole call. The exception is a sub-operation a plugin re-routes an item to (such as softDelete() turning a bulk delete into a move), which runs the full single-operation path and can retry. See Retries for what counts as retryable.
const files = new Files({
adapter: s3({ bucket: "uploads" }),
retries: 3,
hooks: {
onRetry(event) {
logger.warn("files retry", {
action: event.type,
key: event.key,
attempt: `${event.attempt}/${event.maxRetries}`,
delayMs: event.delayMs,
code: event.error.code,
});
},
},
});
It fires before the wait, so delayMs is the backoff the SDK is about to sleep, not one already spent. error is the FilesError from the attempt that just failed - always a retryable Provider failure, since the deterministic codes are never retried.
For a conditional call, the event also carries its redacted condition label. Every attempt retains the same native predicate. A lost success response can still make the retry observe a stale predicate, so a terminal error after a retry is an ambiguous outcome that callers should reconcile.
Watching for exhaustion
attempt counts up from 1, and maxRetries is the ceiling for this call - so attempt === maxRetries is the last retry the SDK will schedule. If that one also fails, no further onRetry fires and the call settles through onError and onAction. Use the comparison to count calls that burn through their whole retry budget.
hooks: {
onRetry(event) {
metrics.increment("files.retry", { action: event.type });
if (event.attempt === event.maxRetries) {
metrics.increment("files.retry.exhausted", { action: event.type });
}
},
},
A steady stream of retries on one type is usually a sign of a throttling or availability problem with that provider - surfacing event.error.code alongside the count makes the cause visible.
Like the other hooks, onRetry is fire-and-forget: it can’t change the backoff, cancel the retry, or fail the call - it only observes.
typeFilesActionType
FilesActionTypecondition?ConditionalActionType
ConditionalActionTypekey?string
stringfrom?string
stringto?string
stringattemptnumber
The retry about to be scheduled — `1` is the first retry.
numbermaxRetriesnumber
Total retries allowed for this call.
numberdelayMsnumber
Milliseconds the SDK will wait before that attempt.
numbererrorFilesError
The error that triggered the retry.
FilesError