dotnet/efcore · error · Exception

Failed to download after attempts.

Error message

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

What it means

Raised by download_file after the while loop exhausts max_retries (default 3). It is only reached for the retryable exception types asyncio.CancelledError, asyncio.TimeoutError, and aiohttp.ClientError; integrity (620) and non-200 (621) failures bypass it. It is the terminal 'I retried and gave up' error for network-layer problems.

Solutions

  1. Check connectivity to the mirror: curl -I {mirror}.
  2. Increase timeout and retries by passing max_retries/timeout at the call site in download_deb_files_parallel (the CLI does not expose them).
  3. Switch to a closer/official mirror or a snapshot mirror.
  4. Fix DNS and HTTP_PROXY/HTTPS_PROXY environment.
  5. Reduce parallelism if the mirror is rate-limiting connections.

Example fix

// before
tasks.append(asyncio.create_task(download_file(session, url, dest_path, checksum=info.get("SHA256"))))

// after
tasks.append(asyncio.create_task(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

# preflight: basic reachability of the mirror before starting downloads
import urllib.request
try:
    with urllib.request.urlopen(f"{mirror}/", timeout=10) as r:
        assert r.status == 200
except Exception:
    sys.exit("mirror unreachable, fix network/DNS/proxy before running")

Try / catch

for run in range(3):
    try:
        asyncio.run(install_packages(...))
        break
    except Exception as e:
        if "after" in str(e) and "attempts" in str(e):
            continue  # network exhaustion -> back off and retry the whole pipeline
        raise
else:
    raise SystemExit("downloads failed after repeated full-run retries")

Prevention

When it happens

Trigger: Every attempt failed with a connection/timeout/DNS error (aiohttp.ClientError or asyncio.TimeoutError) for max_retries consecutive tries with retry_delay seconds between them.

Common situations: No or flaky network, mirror is down or rate-limiting, DNS misconfigured, firewall blocking the mirror, or the default 60s timeout is too short for a large .deb over a slow link.

Related errors


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

Appendix: 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 3a2006ef56)