{"record":{"id":"6b10c30ff60cbd35","repo":"dotnet/aspnetcore","slug":"failed-to-download-url-after-max-retries-attem","errorCode":null,"errorMessage":"Failed to download {url} after {max_retries} attempts.","messagePattern":"Failed to download (.+?) after (.+?) attempts\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"eng/common/cross/install-debs.py","lineNumber":47,"sourceCode":"\n                        # verify checksum if provided\n                        if checksum:\n                            sha256 = hashlib.sha256(content).hexdigest()\n                            if sha256 != checksum:\n                                raise Exception(f\"SHA256 mismatch for {url}: expected {checksum}, got {sha256}\")\n\n                        f.write(content)\n                    print(f\"Downloaded {url} at {dest_path}\")\n                    return\n                else:\n                    raise Exception(f\"Failed to download {url}, Status Code: {response.status}\")\n        except (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError) as e:\n            print(f\"Error downloading {url}: {type(e).__name__} - {e}. Retrying...\")\n\n        attempt += 1\n        await asyncio.sleep(retry_delay)\n\n    raise Exception(f\"Failed to download {url} after {max_retries} attempts.\")\n\nasync def download_deb_files_parallel(mirror, packages, tmp_dir):\n    \"\"\"Download .deb files in parallel.\"\"\"\n    os.makedirs(tmp_dir, exist_ok=True)\n\n    tasks = []\n    timeout = aiohttp.ClientTimeout(total=60)\n    async with aiohttp.ClientSession(timeout=timeout) as session:\n        for pkg, info in packages.items():\n            filename = info.get(\"Filename\")\n            if filename:\n                url = f\"{mirror}/{filename}\"\n                dest_path = os.path.join(tmp_dir, os.path.basename(filename))\n                tasks.append(asyncio.create_task(download_file(session, url, dest_path, checksum=info.get(\"SHA256\"))))\n\n        await asyncio.gather(*tasks)\n\nasync def download_package_index_parallel(mirror, arch, suites, check_sig, keyring):","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/eng/common/cross/install-debs.py#L29-L65","documentation":"Raised by download_file after the while loop exhausts max_retries (default 3) attempts. Each attempt can fail either via a non-200 status (which raises immediately as error 346) or via a network-level exception (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError) that is caught and retried. This terminal error means every retry was defeated by a network exception.","triggerScenarios":"All attempts to session.get(url) failed with TimeoutError or aiohttp.ClientError (DNS, connection reset, TLS handshake failure) within the configured 60s per-request timeout; or the URL host is unreachable for the run's duration.","commonSituations":"No egress to the mirror host (sandboxed CI without network allow-list); DNS misconfiguration; flaky link with packet loss exceeding the 3-retry budget; mirror under heavy load dropping connections; corporate TLS interception breaking the aiohttp session; clock skew causing TLS cert validation failure.","solutions":["From the same host, run curl -v <url> to confirm raw reachability and TLS.","Increase the budget by passing a larger max_retries/retry_delay if you are invoking download_file directly (the CLI does not expose these), or just re-run the whole script a few times.","Allow-list the mirror hostname in CI firewall / proxy and ensure DNS resolves.","If on a constrained network, pre-stage the .deb files and skip the download step."],"exampleFix":"# before\npython3 install-debs.py --mirror http://ftp.debian.org/debian --suite trixie --arch amd64 --rootfsdir rootfs libc6\n# 'Failed to download ... after 3 attempts.'\n\n# after: confirm reachability, then retry\ncurl -I http://ftp.debian.org/debian/dists/trixie/Release\npython3 install-debs.py --mirror http://deb.debian.org/debian --suite trixie --arch amd64 --rootfsdir rootfs libc6","handlingStrategy":"retry","validationCode":"# Pre-flight reachability check before the run:\npython3 - <<'PY'\nimport socket, sys\nhost = 'ftp.debian.org'\ntry: socket.create_connection((host, 80), 5).close()\nexcept OSError as e: print('no egress:', e); sys.exit(1)\nPY","typeGuard":null,"tryCatchPattern":"# In a wrapper script, retry the whole install-debs.py invocation N times:\nfor i in 1 2 3; do python3 install-debs.py \"$@\" && break || sleep 30; done","preventionTips":["Allow-list the mirror host in CI egress firewall.","Run from a network with stable DNS and no TLS interception.","For air-gapped CI, pre-stage the .deb files and skip the download path."],"tags":["python","network","retry","debian","cross-build"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}