dotnet/runtime · error · Exception

Signature verification failed: {result.stderr.decode('utf-8'

Error message

Signature verification failed: {result.stderr.decode('utf-8')}

What it means

With --force-check-gpg, install-debs.py downloads Release and Release.gpg and runs gpgv to verify the detached signature against a keyring. If gpgv exits non-zero (expired/revoked/missing key, malformed signature, wrong keyring, or tampered Release), the build refuses to proceed and surfaces gpgv's stderr so the operator can see the exact OpenPGP failure.

Source

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

    release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"

    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
        await download_file(session, release_url, release_file.name)
        await download_file(session, release_gpg_url, release_gpg_file.name)

        print("Verifying signature of Release with Release.gpg.")
        # Use gpgv rather than gpg for verification. gpgv verifies a detached
        # signature against a fixed keyring without involving gpg-agent or
        # keyboxd, which makes it robust on hosts running GnuPG 2.4+ (e.g. Azure
        # Linux) where "gpg --keyring" routes through keyboxd and can fail.
        verify_command = ["gpgv"]
        if keyring:
            verify_command += ["--keyring", keyring]
        verify_command += [release_gpg_file.name, release_file.name]
        result = subprocess.run(verify_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        if result.returncode != 0:
            raise Exception(f"Signature verification failed: {result.stderr.decode('utf-8')}")

        print("Signature verified successfully.")

        with open(release_file.name) as f:
            return f.read()

def parse_release_file(content, path):
    """Parses the Release file and returns sha256 checksum of the specified path."""

    # data looks like this:
    # <checksum>  <size>  <path>
    matches = re.findall(r'^ (\S*) +(\S*) +(\S*)$', content, re.MULTILINE)

    for entry in matches:
        # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64
        if entry[2] == path and len(entry[0]) == 64:
            return entry[0]

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Install the appropriate archive keyring package (e.g. debian-archive-keyring, ubuntu-archive-keyring, debian-ports-archive-keyring) and point --keyring at it.
  2. Update the keyring package to the latest version so it contains the current signing key.
  3. Read the embedded gpgv stderr in the exception - 'NO_PUBKEY' tells you which key id to import.
  4. As a last resort in a trusted environment, drop --force-check-gpg, but prefer fixing the keyring.

Example fix

# before
python3 install-debs.py --force-check-gpg --keyring /tmp/old.kbx --suite trixie ...
# Signature verification failed: NO_PUBKEY ABCD1234...

# after
apt-get install -y debian-archive-keyring
python3 install-debs.py --force-check-gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg --suite trixie ...
Defensive patterns

Strategy: validation

Validate before calling

# Verify the keyring contains the suite's signing key BEFORE the full build.
import subprocess
def keyring_has_key(keyring: str, key_id: str) -> bool:
    rc = subprocess.run(['gpg', '--no-default-keyring', '--keyring', keyring, '--list-keys', key_id],
                        stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    return rc.returncode == 0

# Or test gpgv directly against Release/Release.gpg of the chosen suite.

Try / catch

try:
    main()
except Exception as e:
    if 'Signature verification failed' in str(e):
        print('Install/update the archive keyring, then re-run with --force-check-gpg.')
        sys.exit(2)
    raise

Prevention

When it happens

Trigger: gpgv returns non-zero at install-debs.py:132-135. Caused by: the suite's signing key rotated and your keyring lacks the new one, the keyring path is wrong/empty, the Release.gpg is stale relative to Release, or GnuPG 2.4+ keyboxd routing issues (the comment notes gpgv was chosen specifically to avoid that).

Common situations: Forgetting to pass --keyring or pointing it at an outdated file. Distribution rolled its archive key (Debian does this periodically). Behind a proxy that corrupted Release.gpg. Host with a partially-initialized GnuPG config.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/81ae112157195e05. Report an issue: GitHub.