dotnet/efcore · error · Exception

Failed to download {url} after {max_retries} attempts.

Error message

Failed to download {url} after {max_retries} attempts.

What it means

Raised by download_file after all max_retries attempts (default 3) failed with transient network exceptions - asyncio.CancelledError, asyncio.TimeoutError, or aiohttp.ClientError. Note that an HTTP non-200 raises error 660 immediately and does not count toward these retries; this error is purely for connection/timeout level failures that never produced a usable response.

Source

Thrown at eng/common/cross/install-debs.py:47

                        # verify checksum if provided
                        if checksum:
                            sha256 = hashlib.sha256(content).hexdigest()
                            if sha256 != checksum:
                                raise Exception(f"SHA256 mismatch for {url}: expected {checksum}, got {sha256}")

                        f.write(content)
                    print(f"Downloaded {url} at {dest_path}")
                    return
                else:
                    raise Exception(f"Failed to download {url}, Status Code: {response.status}")
        except (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError) as e:
            print(f"Error downloading {url}: {type(e).__name__} - {e}. Retrying...")

        attempt += 1
        await asyncio.sleep(retry_delay)

    raise Exception(f"Failed to download {url} after {max_retries} attempts.")

async def download_deb_files_parallel(mirror, packages, tmp_dir):
    """Download .deb files in parallel."""
    os.makedirs(tmp_dir, exist_ok=True)

    tasks = []
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        for pkg, info in packages.items():
            filename = info.get("Filename")
            if filename:
                url = f"{mirror}/{filename}"
                dest_path = os.path.join(tmp_dir, os.path.basename(filename))
                tasks.append(asyncio.create_task(download_file(session, url, dest_path, checksum=info.get("SHA256"))))

        await asyncio.gather(*tasks)

async def download_package_index_parallel(mirror, arch, suites, check_sig, keyring):

View on GitHub (pinned to dbf9771522)

Solutions

  1. Confirm basic connectivity to the mirror host: `curl -v <mirror>` / `getent hosts <mirror-host>`.
  2. Call download_file with larger timeout/max_retries/retry_delay arguments to tolerate slow links.
  3. Check proxy/ firewall settings and export http_proxy/https_proxy if a corporate proxy is required.
  4. Switch to a closer or more reliable mirror.

Example fix

# before
await download_file(session, url, dest_path, checksum=info.get("SHA256"))
# after
await download_file(session, url, dest_path, max_retries=5, retry_delay=5, timeout=180, checksum=info.get("SHA256"))
Defensive patterns

Strategy: retry

Validate before calling

# Pre-flight reachability of the mirror host before the parallel downloads
import socket

def ensure_reachable(host, port=443):
    try:
        socket.gethostbyname(host)
        with socket.create_connection((host, port), timeout=10):
            return True
    except OSError as e:
        raise RuntimeError(f"Mirror host {host} unreachable: {e}; check DNS/proxy/firewall")

from urllib.parse import urlparse
h = urlparse(mirror).hostname
ensure_reachable(h)

Try / catch

# Distinguish 'exhausted retries' from HTTP non-200 by message
try:
    await download_file(session, url, dest_path, max_retries=5, retry_delay=5, timeout=180, checksum=checksum)
except Exception as e:
    if "after" in str(e) and "attempts" in str(e):
        # transient network failure - fall back to an alternate mirror
        await download_file(session, alt_url_for(url), dest_path, max_retries=5, timeout=180, checksum=checksum)
    else:
        raise

Prevention

When it happens

Trigger: Every retry attempt of session.get(url) raised a caught network exception: DNS resolution failure, TCP connect timeout, read timeout exceeding the aiohttp.ClientTimeout total, TLS handshake error, or connection reset. After max_retries the loop exits and raises.

Common situations: No internet or restricted network; corporate proxy/firewall blocking the mirror host; DNS misconfigured; the per-call timeout (default 60s) too short for a large Packages.gz or .deb over a slow link; mirror host temporarily unreachable.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/91b0071959ece52a. Report an issue: GitHub.