GitoxideLabs/gitoxide · error · anyhow::Error

Commit at is not signed

Error message

Commit at {rev_spec} is not signed

What it means

Thrown by `verify` in `gix repo commit verify` when `commit.verify_signature()` returns `None`, meaning the commit object carries no PGP/GPG signature at all. There is nothing to verify, so the command fails with this explicit message instead of reporting success.

Solutions

  1. Re-commit with signing: `git commit --amend -S` (or set `git config commit.gpgsign true`)
  2. Only run verification against commits known to be signed; filter the revision list for signed commits first
  3. If unsigned commits are acceptable, skip verify or treat this message as a no-op result in scripts

Example fix

// before
git commit -m "work"        # unsigned
gix repo commit verify HEAD  # fails
// after
git commit -S -m "work"      # signed
gix repo commit verify HEAD
Defensive patterns

Strategy: validation

Validate before calling

let commit = repo.rev_parse_single(format!("{rev}^{{commit}}"))?.object()?.into_commit();
let signed = commit.decode()?.extra_headers().find("gpgsig").is_some();
if !signed {
    return Err("commit is not signed; nothing to verify".into());
}

Type guard

fn is_signed(commit: &gix::objs::CommitRef) -> bool {
    commit.extra_headers().find("gpgsig").is_some()
}

Try / catch

match verify_commit(rev) {
    Err(e) if e.to_string().contains("is not signed") => {
        // record as unsigned, continue policy-appropriate handling
    }
    other => other?,
}

Prevention

When it happens

Trigger: Running `gix repo commit verify <rev>` on a commit that was created without signing (no `gpgsig` header), e.g. `git commit` without `-S`.

Common situations: Verifying commits in repos where commit signing was never enabled (`commit.gpgsign=false`); testing unsigned history; CI checking signatures on repos with mixed signed/unsigned commits.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/1b5a6d639414be0e. Report an issue: GitHub.

Appendix: source

Thrown at gitoxide-core/src/repository/commit.rs:25

use anyhow::{Context, Result, anyhow, bail};
use gix::{
    bstr::{BStr, BString},
    objs::commit::SIGNATURE_FIELD_NAME,
};

/// Note that this is a quick implementation of commit signature verification that ignores a lot of what
/// git does and can do, while focussing on the gist of it.
/// For this to go into `gix`, one will have to implement many more options and various validation programs.
pub fn verify(repo: gix::Repository, rev_spec: Option<&str>) -> Result<()> {
    let rev_spec = rev_spec.unwrap_or("HEAD");
    let commit = repo
        .rev_parse_single(format!("{rev_spec}^{{commit}}").as_str())?
        .object()?
        .into_commit();
    let outcome = commit
        .verify_signature()
        .context("Could not verify commit signature")?
        .ok_or_else(|| anyhow!("Commit at {rev_spec} is not signed"))?;
    std::io::stderr().write_all(&outcome.output)?;
    if !outcome.is_valid() {
        bail!("Commit at {rev_spec} has an invalid or untrusted signature");
    }
    Ok(())
}

/// Note that this is a quick first prototype that lacks some of the features provided by `git verify-commit`.
pub fn sign(repo: gix::Repository, rev_spec: Option<&str>, mut out: impl std::io::Write) -> Result<()> {
    let rev_spec = rev_spec.unwrap_or("HEAD");
    let object = repo
        .rev_parse_single(format!("{rev_spec}^{{commit}}").as_str())?
        .object()?;
    let mut commit_ref = object.to_commit_ref();
    if commit_ref.extra_headers().pgp_signature().is_some() {
        gix::trace::info!("The commit {id} is already signed, did nothing", id = object.id);
        writeln!(out, "{id}", id = object.id)?;
        return Ok(());

View on GitHub (pinned to e73179060b)