gitbutlerapp/gitbutler · error
Only local branches under refs/heads/ are supported
Error message
Only local branches under refs/heads/ are supported
What it means
normalize_local_branch_ref only accepts bare branch names or full refs under refs/heads/. A ref under refs/ but outside refs/heads/ (e.g. refs/remotes/..., refs/tags/...) cannot denote a local branch, so it is rejected early.
Solutions
- Use the bare local branch name (e.g. `main`) instead of a full ref under refs/remotes or refs/tags.
- If you must pass a full ref, ensure it starts with refs/heads/.
- Strip the remote prefix and create a local tracking branch if needed.
Example fix
// before but branch update refs/remotes/origin/main --to abc123 // after but branch update main --to abc123
Defensive patterns
Strategy: validation
Validate before calling
if (ref.startsWith('refs/') && !ref.startsWith('refs/heads/')) {
throw new Error(`Only refs under refs/heads/ are accepted; got '${ref}'`);
} Prevention
- Pass bare branch names rather than full ref paths.
- Normalize any tooling-supplied refs to refs/heads/<name> or the short name.
- Handle remote-tracking and tag refs separately in scripts before calling but.
When it happens
Trigger: Calling `but branch update` with an argument starting with `refs/` but not `refs/heads/`, such as `refs/remotes/origin/main` or `refs/tags/v1.0`.
Common situations: Copy-pasting full ref names from git tooling output; scripts built around remote-tracking refs; passing tags by full ref path.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Branch ' ' not found
- Branch ' ' not found when checking for conflicts
- Expected a local branch, but
- Expected a local branch, but
- Local branch ' ' not found
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/709222de52a438fa.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/command/branch/update.rs:160
Some(Category::LocalBranch) => Ok(reference.name),
Some(Category::RemoteBranch) => {
bail!(
"Expected a local branch, but '{}' is a remote-tracking branch",
reference.name.shorten()
)
}
_ => bail!(
"Expected a local branch, but '{}' is not under refs/heads/",
reference.name.shorten()
),
}
}
fn normalize_local_branch_ref(branch: &str) -> anyhow::Result<FullName> {
let full = if branch.starts_with("refs/heads/") {
branch.to_owned()
} else if branch.starts_with("refs/") {
bail!("Only local branches under refs/heads/ are supported");
} else {
format!("refs/heads/{branch}")
};
FullName::try_from(full).map_err(|err| anyhow::anyhow!("Invalid branch name: {err}"))
}
fn output_apply_result(
branch_ref: &FullNameRef,
divergence: &but_workspace::branch::IntegrationDivergenceDisplay,
dry_run: bool,
verbose: bool,
result: IntegrateBranchResult,
out: &mut OutputChannel,
) -> anyhow::Result<()> {
if let Some(out) = out.for_human() {
if dry_run {
write!(
out,View on GitHub (pinned to 58e5313667)