{"record":{"id":"2cba2a14247ce688","repo":"GitoxideLabs/gitoxide","slug":"commit-at-rev-spec-has-an-invalid-or-untrusted-s","errorCode":null,"errorMessage":"Commit at {rev_spec} has an invalid or untrusted signature","messagePattern":"Commit at (.+?) has an invalid or untrusted signature","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitoxide-core/src/repository/commit.rs","lineNumber":28,"sourceCode":"    objs::commit::SIGNATURE_FIELD_NAME,\n};\n\n/// Note that this is a quick implementation of commit signature verification that ignores a lot of what\n/// git does and can do, while focussing on the gist of it.\n/// For this to go into `gix`, one will have to implement many more options and various validation programs.\npub fn verify(repo: gix::Repository, rev_spec: Option<&str>) -> Result<()> {\n    let rev_spec = rev_spec.unwrap_or(\"HEAD\");\n    let commit = repo\n        .rev_parse_single(format!(\"{rev_spec}^{{commit}}\").as_str())?\n        .object()?\n        .into_commit();\n    let outcome = commit\n        .verify_signature()\n        .context(\"Could not verify commit signature\")?\n        .ok_or_else(|| anyhow!(\"Commit at {rev_spec} is not signed\"))?;\n    std::io::stderr().write_all(&outcome.output)?;\n    if !outcome.is_valid() {\n        bail!(\"Commit at {rev_spec} has an invalid or untrusted signature\");\n    }\n    Ok(())\n}\n\n/// Note that this is a quick first prototype that lacks some of the features provided by `git verify-commit`.\npub fn sign(repo: gix::Repository, rev_spec: Option<&str>, mut out: impl std::io::Write) -> Result<()> {\n    let rev_spec = rev_spec.unwrap_or(\"HEAD\");\n    let object = repo\n        .rev_parse_single(format!(\"{rev_spec}^{{commit}}\").as_str())?\n        .object()?;\n    let mut commit_ref = object.to_commit_ref();\n    if commit_ref.extra_headers().pgp_signature().is_some() {\n        gix::trace::info!(\"The commit {id} is already signed, did nothing\", id = object.id);\n        writeln!(out, \"{id}\", id = object.id)?;\n        return Ok(());\n    }\n\n    let mut cmd: std::process::Command = gix::command::prepare(\"gpg\").into();","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gitoxide-core/src/repository/commit.rs#L10-L46","documentation":"`verify_commit` ran GPG verification of the commit's signature; either the signature check completed but the result was invalid/untrusted (`outcome.is_valid() == false`), after stderr already received gpg's raw output. Distinguishes from the unsigned case, which raises a separate error.","triggerScenarios":"Calling `verify` on a commit whose signature fails cryptographic validation, was made with an unknown/expired/revoked key, or whose trust level is insufficient.","commonSituations":"Missing or expired GPG keys in the local keyring; signed commits from contributors whose keys aren't imported; verifying on a machine without `gpg` configured; pinentry issues in non-interactive CI.","solutions":["Import the signer's public key (`gpg --import` or from a keyserver) and re-verify","Check for expired/revoked keys and refresh (`gpg --refresh-keys`)","Inspect the gpg output on stderr, which this function writes before failing, for the exact failure reason","If the signature is genuinely bad, treat the commit as untrusted — do not bypass"],"exampleFix":"// before\nverify(repo, rev_spec, &mut out)?;\n// after\nmatch verify(repo, rev_spec, &mut out) {\n    Ok(()) => println!(\"signature OK\"),\n    Err(e) if e.to_string().contains(\"invalid or untrusted signature\") => {\n        eprintln!(\"commit signature invalid — fetch signer key and retry\");\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":"// pre-check the key exists locally\nlet out = std::process::Command::new(\"gpg\").args([\"--list-keys\"]).output()?;\nif !out.status.success() { anyhow::bail!(\"gpg keyring not initialized\"); }","typeGuard":null,"tryCatchPattern":"match verify(repo, rev_spec, &mut err) {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"is not signed\") => eprintln!(\"commit unsigned\"),\n    Err(e) => return Err(e.into_error()),\n}","preventionTips":["Import contributor signing keys ahead of verification","Run gpg with an accessible TTY or loopback pinentry in CI","Refresh keys periodically to avoid expiry-based failures"],"tags":["git","gpg","signature","verification"],"backgroundTag":"authentication-required","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}