boto/boto3 · error · RetriesExceededError

Max Retries Exceeded

Error message

Max Retries Exceeded

What it means

Raised as boto3.exceptions.RetriesExceededError when the managed download exhausts all retry attempts. download_file catches s3transfer's RetriesExceededError and re-raises it as the boto3 RetriesExceededError (with default message 'Max Retries Exceeded') so callers catching the boto3 exception still work; e.last_exception holds the final underlying exception that retries could not overcome.

Source

Thrown at boto3/s3/transfer.py:491

        """
        if isinstance(filename, PathLike):
            filename = fspath(filename)
        if not isinstance(filename, str):
            raise ValueError('Filename must be a string or a path-like object')

        subscribers = self._get_subscribers(callback)
        future = self._manager.download(
            bucket, key, filename, extra_args, subscribers
        )
        try:
            future.result()
        # This is for backwards compatibility where when retries are
        # exceeded we need to throw the same error from boto3 instead of
        # s3transfer's built in RetriesExceededError as current users are
        # catching the boto3 one instead of the s3transfer exception to do
        # their own retries.
        except S3TransferRetriesExceededError as e:
            raise RetriesExceededError(e.last_exception)

    def _get_subscribers(self, callback):
        if not callback:
            return None
        return [ProgressCallbackInvoker(callback)]

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self._manager.__exit__(*args)


class ProgressCallbackInvoker(BaseSubscriber):
    """A back-compat wrapper to invoke a provided callback via a subscriber

    :param callback: A callable that takes a single positional argument for
        how many bytes were transferred.

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Inspect e.last_exception to see the underlying failure (transport, ClientError code) that retries could not clear.
  2. Increase retries via botocore Config: boto3.client('s3', config=Config(retries={'max_attempts': 10, 'mode': 'adaptive'})).
  3. For throttling (503 Slow Down), reduce TransferConfig max_concurrency / max_request_concurrency and switch retry mode to 'adaptive'.
  4. For network instability, add outer application-level retry with backoff around download_file, and consider resumable ranged GETs.
  5. Verify region/endpoint and credentials are correct so failures are not due to misconfiguration.

Example fix

// before
s3.download_file('bkt', 'key', '/tmp/out.bin')  # raises RetriesExceededError

// after
from botocore.config import Config
cfg = Config(retries={'max_attempts': 10, 'mode': 'adaptive'})
s3 = boto3.client('s3', config=cfg)
from boto3.exceptions import RetriesExceededError
for attempt in range(3):
    try:
        s3.download_file('bkt', 'key', '/tmp/out.bin')
        break
    except RetriesExceededError as e:
        log.warning('download failed (%s), retrying', e.last_exception)
Defensive patterns

Strategy: retry

Validate before calling

# Configure a generous retry budget up front so exhaustion is rare
from botocore.config import Config
cfg = Config(retries={'max_attempts': 10, 'mode': 'adaptive'})
s3 = boto3.client('s3', config=cfg)

Type guard

def is_transient(code: str) -> bool:
    return code in ('RequestTimeout', 'RequestTimeoutException', 'SlowDown', 'Throttling', 'ThrottlingException', 'InternalError')

Try / catch

from boto3.exceptions import RetriesExceededError
import time
for attempt in range(3):
    try:
        s3.download_file(bucket, key, path)
        break
    except RetriesExceededError as e:
        last = e.last_exception
        time.sleep(2 ** attempt)
else:
    raise

Prevention

When it happens

Trigger: Calling s3.download_file(bucket, key, filename) (or Bucket/Object variants) where every retry attempt fails — persistent connection resets, sustained 5xx/timeouts, DNS failures, or a transient error that does not resolve within the configured retry budget. Streaming downloads cannot be retried by botocore, so s3transfer performs the retries and reports exhaustion here.

Common situations: Unstable network or restrictive egress (corporate proxy, NAT timeouts); S3 throttling (503 Slow Down) sustained beyond the retry budget; misconfigured region/endpoint causing repeated connection failures; very large multipart downloads over flaky links.

Related errors


AI-assisted analysis of boto/boto3@c7b4afac23 (2026-08-04). Data as JSON: /data/errors/97f13c304134d272.json. Report an issue: GitHub.