{"record":{"id":"680fc3cd9f7df2f9","repo":"jdx/mise","slug":"no-signature-found-to-verify","errorCode":null,"errorMessage":"no signature found to verify","messagePattern":"no signature found to verify","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/gpg.rs","lineNumber":45,"sourceCode":"/// `public_keys_asc` is one or more ASCII-armored public key blocks (a trusted keyring bundled\n/// with mise). `open_data` returns a fresh reader over the signed content each time it is called,\n/// so the content can be streamed (and, if necessary, re-read for another candidate key) without\n/// buffering large files in memory.\n///\n/// Verification succeeds if any signature validates against any of the trusted keys or their\n/// subkeys, mirroring `gpg --verify` against an imported keyring.\nfn verify_detached<R, F>(public_keys_asc: &str, signature: &[u8], open_data: F) -> Result<()>\nwhere\n    R: Read,\n    F: Fn() -> Result<R>,\n{\n    let keys = parse_public_keys(public_keys_asc)?;\n    if keys.is_empty() {\n        bail!(\"no trusted public keys available for verification\");\n    }\n    let signatures = parse_signatures(signature)?;\n    if signatures.is_empty() {\n        bail!(\"no signature found to verify\");\n    }\n\n    // Fast path: only try keys whose id/fingerprint matches the signature's issuer, so the signed\n    // content is hashed at most once in the common case.\n    for sig in &signatures {\n        if verify_against_keys(sig, &keys, &open_data, true)? {\n            return Ok(());\n        }\n    }\n    // Fallback: try every trusted key, but only for signatures that carried no usable issuer\n    // hint. A signature that named an issuer we don't trust is genuinely unverifiable, so skip it\n    // rather than re-hashing the (potentially large) content against every key.\n    for sig in signatures.iter().filter(|sig| !has_issuer(&sig.signature)) {\n        if verify_against_keys(sig, &keys, &open_data, false)? {\n            return Ok(());\n        }\n    }\n    bail!(\"signature does not match any trusted public key\");","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/gpg.rs#L27-L63","documentation":"After parsing the trusted keys, `verify_detached` parses the detached signature blob and bails with \"no signature found to verify\" if it contains no recognizable signature packets. Without at least one signature there is nothing to check against the downloaded file.","triggerScenarios":"Calling verify_detached (via verify_node/verify_swift/verify_swift_bytes) with a .sig file that is empty, truncated, an HTML/text error page, or contains no OpenPGP signature packets.","commonSituations":"Download of the .sig file failed and saved an error page; upstream removed/renamed the signature asset; mixing up the checksum file with the signature file; a partial download cut off mid-file.","solutions":["Re-download the .sig file and confirm it contains '-----BEGIN PGP SIGNATURE-----' before verifying.","Verify you're passing the signature file, not the checksums file or the artifact itself.","Check the URL/asset naming for the signature — upstream may have renamed it in a new release.","Check disk space/transfer completeness if the file may have been truncated."],"exampleFix":"// before\nlet sig = std::fs::read_to_string(\"node.tar.xz.sig\")?; // could be an error page\nverify_detached(&keys, &sig, &data)?;\n// after\nlet sig = std::fs::read_to_string(\"node.tar.xz.sig\")?;\neyre::ensure!(sig.contains(\"BEGIN PGP SIGNATURE\"), \"downloaded .sig is not a signature\");\nverify_detached(&keys, &sig, &data)?;","handlingStrategy":"validation","validationCode":"fn has_detached_signature(blob: &str) -> bool {\n    blob.contains(\"-----BEGIN PGP SIGNATURE-----\")\n}\n// call before verify_detached\nassert!(has_detached_signature(&signature), \".sig file empty or not a signature — re-download\");","typeGuard":null,"tryCatchPattern":"match verify_detached(&keys, &sig, &data) {\n    Err(e) if e.to_string().contains(\"no signature found\") => {\n        eprintln!(\"signature file invalid; re-fetching\");\n        let sig = re_download_signature(&asset_url)?;\n        verify_detached(&keys, &sig, &data)\n    }\n    other => other,\n}","preventionTips":["Check downloaded .sig files contain a PGP SIGNATURE armor header before use","Don't confuse checksums files with signature files","Verify asset URLs against the current release layout after upstream version bumps"],"tags":["gpg","verification","signature","download","security"],"backgroundTag":"file-read-failed","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}