gitbutlerapp/gitbutler · error · anyhow::Error
'{commit_id_str}' is not a commit
Error message
'{commit_id_str}' is not a commit What it means
Thrown by `but show <id>` resolution: `rev_parse_single` did resolve the input to an object, but `try_into_commit()` failed — the object is a tree, blob, or annotated tag rather than a commit. The show command only renders commits, so non-commit objects are rejected here.
Source
Thrown at crates/but/src/command/legacy/show.rs:64
// Found the branch in a stack
return show_branch(ctx, out, &head_name, verbose, &id_map);
}
}
}
// Not a branch, resolve the commit ID through the IdMap
let cli_ids = id_map.parse_using_context(commit_id_str, ctx)?;
let commit_id = if cli_ids.is_empty() {
// If not found in IdMap, try to parse as a git commit ID directly
let repo = ctx.repo.get()?;
let obj = repo
.rev_parse_single(commit_id_str)
.map_err(|_| anyhow::anyhow!("Commit '{commit_id_str}' not found"))?;
let commit = obj
.object()?
.try_into_commit()
.map_err(|_| anyhow::anyhow!("'{commit_id_str}' is not a commit"))?;
commit.id
} else if cli_ids.len() > 1 {
bail!(
"Commit ID '{}' is ambiguous. Found {} matches",
commit_id_str,
cli_ids.len()
);
} else {
match &cli_ids[0] {
CliId::Commit {
commit: CommitId { commit_id, .. },
id: _,
} => *commit_id,
CliId::Branch(branch) => {
// This is a branch identified by CLI ID, show the branch
return show_branch(ctx, out, &branch.name, verbose, &id_map);
}
_ => {View on GitHub (pinned to caf1f223d3)
Solutions
- Resolve to the commit explicitly: `git rev-parse <id>^{commit}` and pass that sha
- For tags, pass the tag name or use the peel syntax `<tag>^{}` instead of the tag object sha
- If you really wanted to inspect a blob or tree, use `git show <sha>` / `git cat-file -p <sha>` — the but command is commit-only
Example fix
# before: tree object sha
but show $(git rev-parse HEAD^{tree})
# after: the commit itself
but show $(git rev-parse HEAD^{commit}) Defensive patterns
Strategy: type-guard
Validate before calling
let obj = repo.rev_parse_single(id.as_bytes())?.object()?;
if obj.kind != gix::objs::ObjectType::Commit {
anyhow::bail!("{id} is a {:?}; pass a commit-ish like '<rev>^{{commit}}'", obj.kind);
} Type guard
fn as_commit_id(obj: gix::objs::ObjectRef<'_>) -> Option<gix::hash::ObjectId> {
obj.try_into_commit().ok().map(|commit| commit.id)}
// or on the id: repo.find_object(id)?.try_into_commit().is_ok() Try / catch
match repo.rev_parse_single(id.as_bytes()) {
Ok(id) => match id.object().and_then(|o| Ok(o.try_into_commit()?)) {
Ok(commit) => { /* show commit */ }
Err(_) => { /* peel: rev_parse "id^{commit}" or reject with guidance */ }
},
Err(err) => return Err(err.into()),
} Prevention
- Always resolve with the `<rev>^{commit}` peel suffix before passing ids around
- Pass branch/tag names rather than raw object shas when the command accepts them
- Use `git cat-file -t <sha>` to check an object's type before pasting it
When it happens
Trigger: Passing a tree id (`git rev-parse HEAD^{tree}`), a blob sha from diff output, or an annotated tag object id where the command expects a commit-ish.
Common situations: Pasting the tree hash shown in `git write-tree` or cat-file output; confusion between `<tag>` (peels fine when passed by name) and the tag object's own sha; tooling that emits whatever oid it saw last.
Related errors
- Commit '{commit_id_str}' not found
- Editor exited with non-zero status
- The repository at {} is bare. GitButler requires a non-bare
- The repository at {} is a non-main worktree. GitButler requi
- The repository at {} has no working directory. GitButler req
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/d2d64eb3aeacb969.
Report an issue: GitHub.