{"id":"97f13c304134d272","repo":"boto/boto3","slug":"max-retries-exceeded","errorCode":null,"errorMessage":"Max Retries Exceeded","messagePattern":"Max Retries Exceeded","errorType":"exception","errorClass":"RetriesExceededError","httpStatus":null,"severity":"error","filePath":"boto3/s3/transfer.py","lineNumber":491,"sourceCode":"        \"\"\"\n        if isinstance(filename, PathLike):\n            filename = fspath(filename)\n        if not isinstance(filename, str):\n            raise ValueError('Filename must be a string or a path-like object')\n\n        subscribers = self._get_subscribers(callback)\n        future = self._manager.download(\n            bucket, key, filename, extra_args, subscribers\n        )\n        try:\n            future.result()\n        # This is for backwards compatibility where when retries are\n        # exceeded we need to throw the same error from boto3 instead of\n        # s3transfer's built in RetriesExceededError as current users are\n        # catching the boto3 one instead of the s3transfer exception to do\n        # their own retries.\n        except S3TransferRetriesExceededError as e:\n            raise RetriesExceededError(e.last_exception)\n\n    def _get_subscribers(self, callback):\n        if not callback:\n            return None\n        return [ProgressCallbackInvoker(callback)]\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, *args):\n        self._manager.__exit__(*args)\n\n\nclass ProgressCallbackInvoker(BaseSubscriber):\n    \"\"\"A back-compat wrapper to invoke a provided callback via a subscriber\n\n    :param callback: A callable that takes a single positional argument for\n        how many bytes were transferred.","sourceCodeStart":473,"sourceCodeEnd":509,"githubUrl":"https://github.com/boto/boto3/blob/c7b4afac237b976d48395d7523eaf7cec3a450b3/boto3/s3/transfer.py#L473-L509","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect e.last_exception to see the underlying failure (transport, ClientError code) that retries could not clear.","Increase retries via botocore Config: boto3.client('s3', config=Config(retries={'max_attempts': 10, 'mode': 'adaptive'})).","For throttling (503 Slow Down), reduce TransferConfig max_concurrency / max_request_concurrency and switch retry mode to 'adaptive'.","For network instability, add outer application-level retry with backoff around download_file, and consider resumable ranged GETs.","Verify region/endpoint and credentials are correct so failures are not due to misconfiguration."],"exampleFix":"// before\ns3.download_file('bkt', 'key', '/tmp/out.bin')  # raises RetriesExceededError\n\n// after\nfrom botocore.config import Config\ncfg = Config(retries={'max_attempts': 10, 'mode': 'adaptive'})\ns3 = boto3.client('s3', config=cfg)\nfrom boto3.exceptions import RetriesExceededError\nfor attempt in range(3):\n    try:\n        s3.download_file('bkt', 'key', '/tmp/out.bin')\n        break\n    except RetriesExceededError as e:\n        log.warning('download failed (%s), retrying', e.last_exception)","handlingStrategy":"retry","validationCode":"# Configure a generous retry budget up front so exhaustion is rare\nfrom botocore.config import Config\ncfg = Config(retries={'max_attempts': 10, 'mode': 'adaptive'})\ns3 = boto3.client('s3', config=cfg)","typeGuard":"def is_transient(code: str) -> bool:\n    return code in ('RequestTimeout', 'RequestTimeoutException', 'SlowDown', 'Throttling', 'ThrottlingException', 'InternalError')","tryCatchPattern":"from boto3.exceptions import RetriesExceededError\nimport time\nfor attempt in range(3):\n    try:\n        s3.download_file(bucket, key, path)\n        break\n    except RetriesExceededError as e:\n        last = e.last_exception\n        time.sleep(2 ** attempt)\nelse:\n    raise","preventionTips":["Use Config(retries={'mode':'adaptive'}) to back off under throttling.","Lower TransferConfig max_concurrency for very large files on flaky links to reduce per-part failures.","Add outer backoff around download_file for resilient pipelines.","Verify region/endpoint and credentials so retries are not wasted on misconfiguration."],"tags":["boto3","s3","download","retry","network"],"analyzedSha":"c7b4afac237b976d48395d7523eaf7cec3a450b3","analyzedAt":"2026-08-04T20:35:51.598Z","schemaVersion":2}