vercel/ai · error · RetryError

Failed after ${tryNumber} attempts. Last error: ${errorMessa

Error message

Failed after ${tryNumber} attempts. Last error: ${errorMessage}

What it means

The SDK's retry helper wraps an operation with exponential backoff. When every retry attempt fails and the number of recorded failures (tryNumber) exceeds maxRetries, it gives up and throws a retry error with reason 'maxRetriesExceeded'. The message embeds the total attempt count and the message of the last underlying error, so the real failure cause is preserved as the 'last error' text (and the original errors array is attached to the created error).

Source

Thrown at packages/provider-utils/src/retry-with-exponential-backoff.ts:102

  errors: unknown[] = [],
): Promise<OUTPUT> {
  try {
    return await f();
  } catch (error) {
    if (isAbortError(error)) {
      throw error; // don't retry when the request was aborted
    }

    if (maxRetries === 0) {
      throw error; // don't wrap the error when retries are disabled
    }

    const errorMessage = getErrorMessage(error);
    const newErrors = [...errors, error];
    const tryNumber = newErrors.length;

    if (tryNumber > maxRetries) {
      throw createRetryError({
        message: `Failed after ${tryNumber} attempts. Last error: ${errorMessage}`,
        reason: 'maxRetriesExceeded',
        errors: newErrors,
      });
    }

    if ((await shouldRetry(error)) && tryNumber <= maxRetries) {
      await delay(
        getDelayInMs({
          error,
          exponentialBackoffDelay: delayInMs,
        }),
        { abortSignal },
      );

      return retryWithExponentialBackoffInternal(
        f,
        {

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Read the 'Last error: ...' portion of the message and the errors array on the thrown retry error to find the root cause, then fix that underlying error first.
  2. If the root cause is rate limiting, reduce request concurrency or add backoff-friendly getDelayInMs and increase maxRetries.
  3. If the root cause is auth or bad request, fix credentials/request before retrying; also tighten shouldRetry so non-retryable errors (e.g. 401/400) fail fast.
  4. If the operation is expected to fail and wrapping obscures it, set maxRetries: 0 so the original error is thrown unwrapped.

Example fix

// before: opaque wrapped failure
const result = await retryWithExponentialBackoff({ maxRetries: 5, shouldRetry })(() => fetch(url));
// after: fail fast on non-retryable statuses and log root cause
const result = await retryWithExponentialBackoff({
  maxRetries: 3,
  shouldRetry: ({ error }) => error?.statusCode == null || error.statusCode >= 500 || error.statusCode === 429,
})(() => fetch(url));
Defensive patterns

Strategy: try-catch

Validate before calling

// check retry budget vs expected flakiness before wrapping
if (maxRetries < 1) throw new Error('maxRetries must be >= 1 to use retryWithExponentialBackoff');

Try / catch

try {
  return await retryFn(() => call());
} catch (error) {
  // inspect last error text / attached errors array
  const causes = (error as any)?.errors ?? [];
  console.error('All attempts failed:', error.message, causes);
  if (isAbortError(error)) throw error; // aborted calls are rethrown unwrapped
  throw error;
}

Prevention

When it happens

Trigger: A call wrapped by retryWithExponentialBackoff (used internally by provider fetches when maxRetries > 0) throws on every attempt, and each failure passes the shouldRetry predicate, until newErrors.length > maxRetries. E.g. maxRetries: 2 means attempts 1,2 are retried and the error is thrown once tryNumber reaches 3.

Common situations: Provider API outage or sustained 429/5xx responses; invalid API key causing repeated 401s where shouldRetry still allows retrying; network connectivity loss; misconfigured maxRetries being high while the endpoint is permanently failing; request payload rejected (400) by a provider with a retryable-classification bug.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/c3b7a3dc2ceb6187. Report an issue: GitHub.