gitbutlerapp/gitbutler · error
" " does not refer to a commit or a branch
Error message
"{target}" does not refer to a commit or a branch What it means
Thrown by parse_commit_or_branch when the IdMap matched exactly one CliId, but that id is neither a Commit nor a Branch (e.g. a file/htag/hunk id). The target resolves to something in the workspace, just not an entity that can carry conflicts.
Solutions
- Run `but status` and use a commit or branch identifier instead.
- If the goal is to resolve a file, pass the path in the path/hunk argument position, not as the commit target.
Example fix
// before but resolve src/main.rs // after but resolve abc123 src/main.rs
Defensive patterns
Strategy: type-guard
Validate before calling
const matched = idMap.parse(target);
if (matched.length === 1 && matched[0].kind !== 'commit' && matched[0].kind !== 'branch') {
throw new Error('target must be a commit or branch, not ' + matched[0].kind);
} Type guard
const isCommit = (id: CliId): id is CommitCliId => id.kind === 'commit'; const isBranch = (id: CliId): id is BranchCliId => id.kind === 'branch';
Try / catch
try { await butResolve(target) } catch (e) { if (String(e).includes('does not refer to a commit or a branch')) { hint('pass a commit or branch id; the path goes as a separate argument'); } } Prevention
- Remember argument order: commit/branch target first, file path second.
- Never pass file paths or hunk ids as the commit target.
- Check id kind in `but status` output.
When it happens
Trigger: Passing a filename, hunk marker, or other non-commit/non-branch CliId as the conflict target to `but resolve`.
Common situations: Users paste a file path or hunk reference (like in `but resolve file.rs`) when the command in this position expects a commit or branch identifier.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- ' ' is not a commit
- " " is ambiguous. Please provide more characters to…
- " " is neither a commit nor a branch. Try running 'but…
- Aborting due to empty branch name
- Bare repositories are not supported.
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/b1537bdb2bb9a6a1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/command/legacy/resolve.rs:713
}))
}
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,
"{}",
t.success.paint("No conflicted commits found.")
)?;
}
if let Some(json_out) = out.for_json() {View on GitHub (pinned to 58e5313667)