{"record":{"id":"8b2fea2ff9f79cc5","repo":"dotnet/aspnetcore","slug":"sha256-mismatch-for-url-expected-checksum-go","errorCode":null,"errorMessage":"SHA256 mismatch for {url}: expected {checksum}, got {sha256}","messagePattern":"SHA256 mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"eng/common/cross/install-debs.py","lineNumber":34,"sourceCode":"\nfrom collections import deque\nfrom functools import cmp_to_key\n\nasync def download_file(session, url, dest_path, max_retries=3, retry_delay=2, timeout=60, checksum=None):\n    \"\"\"Asynchronous file download with retries.\"\"\"\n    attempt = 0\n    while attempt < max_retries:\n        try:\n            async with session.get(url, timeout=aiohttp.ClientTimeout(total=timeout)) as response:\n                if response.status == 200:\n                    with open(dest_path, \"wb\") as f:\n                        content = await response.read()\n\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","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/eng/common/cross/install-debs.py#L16-L52","documentation":"install-debs.py's download_file computes the SHA256 of the just-downloaded bytes and raises a plain Exception when it differs from the expected checksum supplied by the Packages index. This is integrity verification for individual .deb files pulled from the Debian/Ubuntu mirror. A mismatch means the bytes received are not the bytes the index recorded — possible tampering, a stale/republished package, or a transparent proxy serving wrong content.","triggerScenarios":"download_file is called with a non-None checksum (which happens for .deb downloads in download_debs_files_parallel, where info.get('SHA256') from the parsed Packages index is passed). The recomputed sha256 differs from the recorded one. The exception propagates out of asyncio.gather and aborts the rootfs build.","commonSituations":"Mirror is mid-sync and serving a mix of old/new packages; a transparent HTTP cache (corporate proxy, container registry mirror) holds stale bytes; the suite/mirror combination points at an archive whose Packages.gz is from a different epoch than the .deb files; partial write corruption.","solutions":["Switch to an official, fully-synced mirror (e.g., http://deb.debian.org/debian or a known-good snapshot) and re-run.","Clear any local HTTP cache/proxy between the host and the mirror, or disable the proxy for this download.","Ensure --suite matches the mirror's actual published suite (mixing bookworm Packages.gz with sid .debs produces mismatches).","If using a snapshot mirror, pin both the index and the debs to the same snapshot timestamp.","Re-run after the mirror finishes syncing; transient mirror skew resolves on its own."],"exampleFix":"# before\npython3 install-debs.py --arch amd64 --suite sid \\\n  --mirror http://some-mirror/debian ...\n# fails: SHA256 mismatch\n\n# after\npython3 install-debs.py --arch amd64 --suite sid \\\n  --mirror http://deb.debian.org/debian ...","handlingStrategy":"retry","validationCode":"# Probe the URL and its expected checksum before invoking download_file\nimport hashlib, urllib.request\nexpected = info.get('SHA256')\nif expected:\n    with urllib.request.urlopen(url) as r:\n        actual = hashlib.sha256(r.read()).hexdigest()\n    if actual != expected:\n        raise SystemExit(f'Pre-flight checksum mismatch for {url}; do not proceed')","typeGuard":null,"tryCatchPattern":"from tenacity import retry, stop_after_attempt, retry_if_exception_type\n\n@retry(stop=stop_after_attempt(5),\n       retry=retry_if_exception_type(Exception),\n       reraise=True)\ndef safe_download(session, url, dest, checksum):\n    try:\n        await download_file(session, url, dest, checksum=checksum)\n    except Exception as e:\n        if 'SHA256 mismatch' in str(e):\n            print(f'Checksum skew for {url}, will retry from a fresh mirror')\n            raise  # tenacity will retry\n        raise","preventionTips":["Pin to a single fully-synced mirror or a snapshot mirror so index and debs are consistent.","Run --force-check-gpg in CI to anchor per-deb checksums to a signed Release.","Clear corporate HTTP caches between rootfs builds.","Log the URL and expected checksum on mismatch to identify the offending mirror."],"tags":["python","debian","network","checksum","integrity","install-debs","rootfs"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}