gitbutlerapp/gitbutler · error
Expected a branch ID, got {}
Error message
Expected a branch ID, got {} What it means
The branch ID given to `but land` resolved uniquely — but to the wrong kind of entity, reported via `kind_for_humans()` (a commit or an uncommitted file instead of a `CliId::Branch`). Land operates on lanes/branches only, so the handler bails naming what it actually got.
Source
Thrown at crates/but/src/command/legacy/land/mod.rs:53
bail!(
"`but land` requires an active GitButler workspace (`gitbutler/workspace`). \
Switch into the workspace and try again."
);
}
}
let id_map = IdMap::new_from_context(ctx, guard.read_permission())?;
let resolved_ids = id_map.parse_using_context(branch_id, ctx)?;
if resolved_ids.is_empty() {
bail!("Could not find branch: {branch_id}");
}
if resolved_ids.len() > 1 {
bail!("Ambiguous branch '{branch_id}', matches multiple items");
}
let branch_name = match &resolved_ids[0] {
CliId::Branch(branch) => branch.name.clone(),
other => bail!("Expected a branch ID, got {}", other.kind_for_humans()),
};
let base_branch =
but_api::legacy::virtual_branches::get_base_branch_data(ctx, guard.write_permission())?
.ok_or_else(|| anyhow::anyhow!("No base branch configured"))?;
(branch_name, base_branch)
};
// Display strings for the prompt and the final report. The API recomputes the target/remote
// configuration internally; the CLI only needs these names to describe what's about to happen.
let target_branch_name = base_branch.short_name.clone();
let push_remote_name = if base_branch.push_remote_name.is_empty() {
base_branch.remote_name.clone()
} else {
base_branch.push_remote_name.clone()
};
let target_display = format!("{push_remote_name}/{target_branch_name}");
View on GitHub (pinned to caf1f223d3)
Solutions
- Use the lane/branch ID shown in the branches section of `but status`.
- Pass the plain branch name instead of an ID.
- If you meant to act on a commit, use a commit-oriented command (e.g. `but resolve <commit>`), not `but land`.
Example fix
# before but land 4z --yes # 4z is a commit ID -> Expected a branch ID, got commit # after but land b2 --yes # b2 is the lane that contains commit 4z
Defensive patterns
Strategy: type-guard
Validate before calling
# Use lane IDs/names only; verify the ID appears in the branches section of `but status` but status | grep -F '<id>'
Type guard
// Narrow before acting: land accepts only branches
match &resolved_ids[0] {
CliId::Branch(branch) => { /* safe to land `branch.name` */ }
other => { /* reject: expected a branch, got `other.kind_for_humans()` */ }
} Prevention
- Check which section of `but status` an ID comes from before passing it to a branch-scoped command.
- Remember the split: `land` takes branches; `resolve`/`squash` take commits.
When it happens
Trigger: `but land <commit-id>` or `but land <uncommitted-file-id>` — IDs that resolve fine, just not to a branch (land/mod.rs:52-54).
Common situations: Grabbing a commit's ID from `but status` output instead of its lane's; muscle memory from commands that do accept commits (e.g. `but resolve <commit>`).
Related errors
- Could not find branch: {branch_id}
- Ambiguous branch '{branch_id}', matches multiple items
- '{commit_id_str}' does not refer to a commit
- No branch found for ID: {branch_id}
- Refusing to directly update {target_display} without confirm
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/70763d7abe669d60.
Report an issue: GitHub.