dotnet/aspnetcore · error · Exception

Failed to download {url}, Status Code: {response.status}

Error message

Failed to download {url}, Status Code: {response.status}

What it means

download_file raises a plain Exception when the HTTP response status is not 200, including the status code in the message. This fires before any retry accounting for the body — non-200 statuses are treated as download failures. The exception is caught by the except clause below (CancelledError/TimeoutError/ClientError) only for those specific types; a status-code Exception is re-raised and terminates the loop because it is not in the caught tuple, so it propagates immediately rather than retrying.

Source

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

    attempt = 0
    while attempt < max_retries:
        try:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=timeout)) as response:
                if response.status == 200:
                    with open(dest_path, "wb") as f:
                        content = await response.read()

                        # 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:

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Confirm the --suite and --arch combination exists on the --mirror (open the dists/<suite>/ URL in a browser).
  2. For 404 on a specific package, the suite may have moved the package — try a different suite or pin to a snapshot mirror.
  3. For 403/429, reduce parallelism or use a different mirror that does not rate-limit.
  4. Note: status-code errors do not retry by design; fix the URL/mirror rather than expecting the retry loop to help.
  5. Temporarily remove --force-check-gpg to isolate whether the failure is the Release/Packages fetch vs the .deb fetch.

Example fix

# before — wrong suite for the package
python3 install-debs.py --suite stretch --mirror http://deb.debian.org/debian ...
# 404 on libc6

# after
python3 install-debs.py --suite bookworm --mirror http://deb.debian.org/debian ...
Defensive patterns

Strategy: try-catch

Validate before calling

# HEAD the URL before the bulk download to fail fast on 404/403
import aiohttp
async with aiohttp.ClientSession() as s:
    async with s.head(url) as h:
        if h.status != 200:
            print(f'Pre-flight {h.status} for {url}; aborting before retry loop')
            return False

Try / catch

try:
    await download_file(session, url, dest, checksum=checksum)
except Exception as e:
    msg = str(e)
    if 'Status Code:' in msg:
        code = int(msg.split('Status Code:')[1].strip())
        if code in (404, 410):
            print(f'Package permanently gone: {url}')
            raise  # not retryable
        # other codes might warrant a mirror switch
    raise

Prevention

When it happens

Trigger: A .deb URL or Release/Packages URL returns 404 (package removed from the suite), 403 (mirror access denied), or 5xx (mirror error). The Exception raised inside the try is NOT in the (CancelledError, TimeoutError, ClientError) tuple, so it escapes the retry loop and surfaces directly.

Common situations: Package was removed from the suite but the Packages index still references it; mirror requires authentication or blocks user-agents; CDN rate-limiting returns 429; wrong --suite producing 404s for the requested components.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/1b4aa6077aa208c5. Report an issue: GitHub.