{"record":{"id":"574eefb3ff2b3d55","repo":"dotnet/runtime","slug":"sha256-mismatch-for-path-expected-packages-sha","errorCode":null,"errorMessage":"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}","messagePattern":"SHA256 mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"eng/common/cross/install-debs.py","lineNumber":105,"sourceCode":"    \"\"\"Fetch and decompress the Packages.gz file.\"\"\"\n\n    path = f\"{component}/binary-{arch}/Packages.gz\"\n    url = f\"{mirror}/dists/{suite}/{path}\"\n\n    async with session.get(url) as response:\n        if response.status == 200:\n            compressed_data = await response.read()\n            decompressed_data = gzip.decompress(compressed_data).decode('utf-8')\n            print(f\"Downloaded index: {url}\")\n\n            if check_sig:\n                # Verify the package index against the sha256 recorded in the Release file\n                release_file_content = await fetch_release_file(session, mirror, suite, keyring)\n                packages_sha = parse_release_file(release_file_content, path)\n\n                sha256 = hashlib.sha256(compressed_data).hexdigest()\n                if sha256 != packages_sha:\n                    raise Exception(f\"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}\")\n                print(f\"Checksum verified for {path}\")\n\n            return decompressed_data\n        else:\n            print(f\"Skipped index: {url} (doesn't exist)\")\n            return None\n\nasync def fetch_release_file(session, mirror, suite, keyring):\n    \"\"\"Fetch Release and Release.gpg files and verify the signature.\"\"\"\n\n    release_url = f\"{mirror}/dists/{suite}/Release\"\n    release_gpg_url = f\"{mirror}/dists/{suite}/Release.gpg\"\n\n    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:\n        await download_file(session, release_url, release_file.name)\n        await download_file(session, release_gpg_url, release_gpg_file.name)\n\n        print(\"Verifying signature of Release with Release.gpg.\")","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/eng/common/cross/install-debs.py#L87-L123","documentation":"When --force-check-gpg is set, install-debs.py hashes the compressed Packages.gz bytes and compares against the SHA-256 recorded for that path in the cryptographically signed Release file. A mismatch means the package index itself is not what the distribution signed - mirror desync, a transparent proxy rewriting responses, or an attack. The build refuses to trust an index whose checksum does not match the signature.","triggerScenarios":"fetch_and_decompress computes SHA-256 of the downloaded Packages.gz content and it differs from parse_release_file(release, path). Raised at install-debs.py:104-105. Happens on mirror replication lag, CDN edge staleness, or proxy interference, when --force-check-gpg is on.","commonSituations":"Mirror mid-update (Release file updated before Packages.gz replicates). HTTP proxy caching stale bodies. CDN layer serving mixed versions. Rare man-in-the-middle tampering.","solutions":["Re-run the rootfs build; mirror desync usually self-heals.","Point --mirror at a different, fully-replicated mirror (e.g. the official deb.debian.org / ports mirror).","Bypass any HTTP proxy that may be caching inconsistent bodies.","If you trust the environment, drop --force-check-gpg (or use --skipsigcheck in build-rootfs.sh), but prefer fixing the mirror."],"exampleFix":"# before\npython3 install-debs.py --force-check-gpg --keyring debian.kbx --suite trixie --mirror http://stale-mirror/debian ...\n# SHA256 mismatch on Packages.gz\n\n# after\npython3 install-debs.py --force-check-gpg --keyring debian.kbx --suite trixie --mirror http://deb.debian.org/debian ...","handlingStrategy":"validation","validationCode":"# Before trusting an index, fetch Release and Packages.gz independently and compare SHA-256.\nimport hashlib, aiohttp, asyncio\nasync def verify_index(mirror, suite, path):\n    async with aiohttp.ClientSession() as s:\n        async with s.get(f'{mirror}/dists/{suite}/Release') as r: rel = await r.text()\n        async with s.get(f'{mirror}/dists/{suite}/{path}') as r: gz = await r.read()\n    expected = parse_release_file(rel, path)\n    actual = hashlib.sha256(gz).hexdigest()\n    return expected == actual","typeGuard":null,"tryCatchPattern":"try:\n    main()\nexcept Exception as e:\n    if 'SHA256 mismatch' in str(e) and 'Packages' in str(e):\n        print('Signed Release disagrees with served Packages.gz; switch --mirror or wait for sync.')\n        sys.exit(2)\n    raise","preventionTips":["Use a fully-replicated official mirror when --force-check-gpg is set.","Avoid HTTP proxies that cache inconsistent bodies for the mirror.","If desync is transient, re-run after a short delay."],"tags":["dotnet-rootfs","debian","sha256","gpg","integrity","python"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}