dotnet/aspnetcore · 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) 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.

Solutions

  1. From the same host, run curl -v <url> to confirm raw reachability and TLS.
  2. 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.
  3. Allow-list the mirror hostname in CI firewall / proxy and ensure DNS resolves.
  4. If on a constrained network, pre-stage the .deb files and skip the download step.

Example fix

# before
python3 install-debs.py --mirror http://ftp.debian.org/debian --suite trixie --arch amd64 --rootfsdir rootfs libc6
# 'Failed to download ... after 3 attempts.'

# after: confirm reachability, then retry
curl -I http://ftp.debian.org/debian/dists/trixie/Release
python3 install-debs.py --mirror http://deb.debian.org/debian --suite trixie --arch amd64 --rootfsdir rootfs libc6
Defensive patterns

Strategy: retry

Validate before calling

# Pre-flight reachability check before the run:
python3 - <<'PY'
import socket, sys
host = 'ftp.debian.org'
try: socket.create_connection((host, 80), 5).close()
except OSError as e: print('no egress:', e); sys.exit(1)
PY

Try / catch

# In a wrapper script, retry the whole install-debs.py invocation N times:
for i in 1 2 3; do python3 install-debs.py "$@" && break || sleep 30; done

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/6b10c30ff60cbd35. 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 3600ca084e)