gitbutlerapp/gitbutler · error
target ref ' ' must be a remote tracking branch
Error message
target ref '{}' must be a remote tracking branch What it means
Initializing a GitButler project's target requires a remote-tracking branch reference, because the target defines what the workspace pushes to and fetches from. If the provided target ref's category is not a remote branch (e.g. a local branch, tag, or other ref), initialization is refused.
Solutions
- Pass the remote-tracking form, e.g. origin/main (refs/remotes/origin/main)
- Run git fetch (or the equivalent gix fetch) first so the remote-tracking branch exists
- If you only have a local branch, push it to a remote or create a tracking branch, then use that
Example fix
// before but.set_target_ref_and_init_project(repo, "refs/heads/main")?; // after but.set_target_ref_and_init_project(repo, "refs/remotes/origin/main")?;
Defensive patterns
Strategy: validation
Validate before calling
let r = repo.find_reference(&target_ref)?; debug_assert_eq!(r.name().category(), Some(gix::refs::Category::RemoteBranch)); // refuse non remote-tracking refs before calling
Type guard
fn is_remote_tracking_ref(name: &gix::refs::PartialNameRef) -> bool {
name.category() == Some(gix::refs::Category::RemoteBranch)
} Try / catch
match result {
Err(e) if e.to_string().contains("remote tracking branch") => {
// convert e.g. heads/main → remotes/origin/main and retry
}
other => other?,
} Prevention
- Always normalize user-supplied branch names to refs/remotes/<remote>/<branch>
- Fetch remotes before project initialization so tracking branches exist
- Validate ref category in input-parsing code, not deep in the call stack
When it happens
Trigger: Calling set_target_ref_and_init_project with a ref whose category() is not gix::refs::Category::RemoteBranch — e.g. refs/heads/main instead of refs/remotes/origin/main.
Common situations: Passing a local branch name by mistake; a repo where the remote-tracking branch hasn't been fetched so only a local branch exists; scripts building full ref names incorrectly.
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
- Can only delete local branches under refs/heads, got
- Expected ' ' to be a remote tracking branch, but was
- Invalid branch_ref_bytes in assignment request for
- Aborting due to empty branch name
- Branch name ' ' collides with existing branch
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/2a83052ef33eb309.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/init.rs:144
/// reachable from that target; otherwise it is replaced by the merge-base with `HEAD`. The target
/// is always validated against `HEAD` before metadata is persisted.
/// `push_remote`, if `Some`, is validated and stored; if `None`, an existing push remote
/// is kept as is.
///
/// Unlike `set_base_branch()`, this neither creates stacks, nor updates the workspace
/// commit, nor checks anything out. The caller is expected to hold exclusive worktree
/// access, and to invalidate any cached workspace projection afterwards.
pub fn set_target_ref_and_init_project(
repo: &gix::Repository,
target_ref: &gix::refs::FullNameRef,
push_remote: Option<String>,
) -> Result<()> {
let project_meta = ProjectMeta::resolve(repo)?;
let repaired =
but_core::ref_metadata::repair_target_metadata_for_migration(&project_meta, repo);
if target_ref.category() != Some(gix::refs::Category::RemoteBranch) {
bail!(
"target ref '{}' must be a remote tracking branch",
target_ref.as_bstr()
);
}
let target_head = repo
.try_find_reference(target_ref)?
.with_context(|| format!("remote branch '{}' not found", target_ref.as_bstr()))?
.peel_to_commit()
.with_context(|| format!("failed to peel branch '{}' to commit", target_ref.as_bstr()))?
.id;
// Reject targets whose remote isn't configured - reads like the base-branch data
// would fail on them later.
let (_upstream_ref, remote) = repo
.upstream_branch_and_remote_for_tracking_branch(target_ref)?
.with_context(|| {
format!(View on GitHub (pinned to 58e5313667)