gitbutlerapp/gitbutler · error
" " is neither a commit nor a branch. Try running 'but…
Error message
"{target}" is neither a commit nor a branch. Try running 'but status' to see what is available. What it means
Thrown by parse_commit_or_branch when the IdMap lookup for the user-supplied target returns no matches, i.e. the string is neither recognizable as a commit nor as a branch in the current workspace. The message redirects the user to `but status` to discover valid identifiers.
Solutions
- Run `but status` to list valid commit/branch identifiers.
- Copy the exact identifier from the status output.
- Check for typos or truncated hashes.
Example fix
// before but resolve feat/auhtorization // after but resolve feat/authorization
Defensive patterns
Strategy: validation
Validate before calling
const ids = parseStatusOutput(runButStatus());
if (!ids.commits.some(c => c.startsWith(target)) && !ids.branches.includes(target)) {
throw new Error(`"${target}" is neither a commit nor a branch`);
} Type guard
const isCommitOrBranch = (id: string, ids: CliId[]) => ids.some(i => (i.kind === 'commit' || i.kind === 'branch') && i.matches(id));
Try / catch
try { await butResolve(target) } catch (e) { if (String(e).includes('neither a commit nor a branch')) { print(runButStatus()); } } Prevention
- Run `but status` first and copy identifiers verbatim.
- Use full branch names rather than guesses.
- Avoid referencing commits from outside the workspace.
When it happens
Trigger: `but resolve <target> ...` where <target> matches zero CliIds in the context IdMap — an unknown commit hash prefix, nonexistent branch, or a reference from outside the workspace.
Common situations: Typos, stale branch names after rebases, using a full SHA that is not reachable in the workspace, or referencing an identifier only valid in another repo.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Branch ' ' not found
- Branch ' ' not found when checking for conflicts
- Branch " " has no conflicted commits.
- Commit ' ' not found
- Commit is not conflicted.
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/76f4028215ae35b8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/command/legacy/resolve.rs:708
.len();
Ok(Some(ConflictTarget {
commit_oid,
branch,
other_conflicted,
}))
}
enum CommitOrBranch {
Commit(gix::ObjectId),
Branch(String),
}
/// Resolve a user-provided identifier to a commit or a branch.
fn parse_commit_or_branch(ctx: &mut Context, target: &str) -> Result<CommitOrBranch> {
let id_map = IdMap::legacy_new_from_context(ctx)?;
let matches = id_map.parse_using_context(target, ctx)?;
match matches.as_slice() {
[] => bail!(
"\"{target}\" is neither a commit nor a branch. Try running 'but status' to see what is available."
),
[CliId::Commit { commit, .. }] => Ok(CommitOrBranch::Commit(commit.commit_id)),
[CliId::Branch(branch)] => Ok(CommitOrBranch::Branch(branch.name.clone())),
[_] => bail!("\"{target}\" does not refer to a commit or a branch"),
_ => bail!(
"\"{target}\" is ambiguous. Please provide more characters to uniquely identify it."
),
}
}
/// List the conflicts of a conflicted commit without entering resolution mode.
fn list_conflicts(ctx: &mut Context, out: &mut OutputChannel, commit: Option<&str>) -> Result<()> {
let t = theme::get();
let Some(target) = target_conflicted_commit(ctx, commit)? else {
if let Some(human_out) = out.for_human() {
writeln!(
human_out,View on GitHub (pinned to 58e5313667)