GitoxideLabs/gitoxide · error

Command failed

Error message

Command {cmd:?} failed

What it means

`sign` pipes the object data into an external signing command's stdin and expects a zero exit status. If the spawned command exits non-zero the function bails. The signing binary failed — bad key, wrong passphrase, missing config — and its stderr is typically inherited/visible.

Solutions

  1. Check the signing command's stderr output for the root cause
  2. Pre-test the command manually (e.g. `echo test | gpg --clearsign`) and unlock the agent
  3. Set `user.signingkey` / `gpg.program` correctly in git config
  4. In CI, provide the key non-interactively (GPG passphrase via loopback pinentry)

Example fix

// before
sign(repo, None, &mut out)?;
// after
std::env::set_var("GPG_TTY", ""); // let gpg prompt, or configure loopback pinentry in CI
sign(repo, None, &mut out)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the signing command works before signing
let probe = std::process::Command::new("gpg").arg("--version").output()?;
if !probe.status.success() { anyhow::bail!("gpg unavailable"); }

Try / catch

if let Err(e) = sign(repo, None, &mut out) {
    eprintln!("signing command failed: {e}; check gpg agent/passphrase");
}

Prevention

When it happens

Trigger: Calling `sign` with `commit.gpgsign`/`user.signingkey` pointing at a missing key, a gpg agent that can't prompt for a passphrase, or any custom `cmd` binary that exits non-zero.

Common situations: Non-interactive CI where gpg can't prompt for the passphrase; misconfigured `user.signingkey`; unsigned-key selection errors; the signing program not installed.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


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

Appendix: source

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

    }

    let mut cmd: std::process::Command = gix::command::prepare("gpg").into();
    cmd.args([
        "--keyid-format=long",
        "--status-fd=2",
        "--detach-sign",
        "--sign",
        "--armor",
    ])
    .stdin(Stdio::piped())
    .stdout(Stdio::piped());

    gix::trace::debug!("About to execute {cmd:?}");
    let mut child = cmd.spawn()?;
    child.stdin.take().expect("to be present").write_all(&object.data)?;

    if !child.wait()?.success() {
        bail!("Command {cmd:?} failed");
    }

    let mut signed_data = Vec::new();
    child.stdout.expect("to be present").read_to_end(&mut signed_data)?;

    commit_ref
        .extra_headers
        .push((BStr::new(SIGNATURE_FIELD_NAME), Cow::Owned(BString::new(signed_data))));

    let signed_id = repo.write_object(&commit_ref)?;
    writeln!(&mut out, "{signed_id}")?;

    Ok(())
}

pub fn describe(
    mut repo: gix::Repository,
    rev_spec: Option<&str>,

View on GitHub (pinned to e73179060b)