dotnet/efcore · critical · Exception
Signature verification failed
Error message
Signature verification failed: {result.stderr.decode('utf-8')} What it means
Raised by fetch_release_file when gpgv exits non-zero verifying Release.gpg against Release using the supplied --keyring. The error string is gpgv's decoded stderr. It means the Release file's detached signature does not validate against the keys present in the keyring, i.e. an authentication failure of the archive metadata.
Solutions
- Install or update the matching archive-keyring package (e.g. debian-ports-archive-keyring for Debian ports, ubuntu-archive-keyring for Ubuntu).
- Confirm --keyring points to the keyring that actually signed the chosen suite (Debian vs Ubuntu vs ports).
- Reproduce manually: gpgv --keyring <keyring> Release.gpg Release and read the full stderr (the script only surfaces it as an Exception message).
- Re-fetch Release/Release.gpg in case of a truncated download.
- As a last resort, drop --force-check-gpg (the script prints guidance pointing to --skipsigcheck).
Example fix
// before --force-check-gpg --keyring /usr/share/keyrings/old-archive-keyring.gpg // after sudo apt-get install -y debian-ports-archive-keyring --force-check-gpg --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg
Defensive patterns
Strategy: validation
Validate before calling
# preflight: ensure the Release signing key is present in the keyring
import subprocess
r = subprocess.run(["gpgv", "--keyring", keyring, release_gpg, release], capture_output=True)
if r.returncode != 0:
sys.exit(f"keyring cannot verify Release; install/update the archive-keyring package: {r.stderr.decode()}") Prevention
- Install and keep updated the archive-keyring package matching your distro (debian-ports-archive-keyring, ubuntu-archive-keyring).
- Pin --keyring to the keyring that signs the chosen suite; do not mix Debian and Ubuntu keyrings.
- Verify signatures manually with gpgv when a new release/key rotation happens.
When it happens
Trigger: --force-check-gpg and --keyring are both set; gpgv --keyring <keyring> Release.gpg Release returns a non-zero exit code.
Common situations: The signing key is missing from the keyring (key rotated, new release signed by a key you do not have), the keyring package (debian-ports-archive-keyring / ubuntu-archive-keyring) is outdated or expired, Release or Release.gpg was corrupted in transit, a MITM, or the wrong keyring was chosen for the suite/distro.
Related errors
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/412afeb8d4adc786.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)