gitbutlerapp/gitbutler · error

failed to determine remote and branch name for `{branch}`

Error message

failed to determine remote and branch name for `{branch}`

What it means

remote_tracking_branch_parts (crates/gitbutler-git/src/context.rs) delegates to but_core::extract_remote_name_and_short_name and errors when it returns None. That helper returns None when the ref is not a refs/remotes/* ref at all, when no configured remote name prefixes the shorthand, and when the fallback for stray remote-tracking refs fails — the fallback assumes the remote is the first slash-separated component and refuses (None) if the remaining short name itself contains a slash (e.g. refs/remotes/unknown/feature/x).

Source

Thrown at crates/gitbutler-git/src/context.rs:325

) -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    branch_to_remote
        .iter()
        .map(|(branch_name, refname, remote_branch_name)| {
            (branch_name, refname.to_string(), remote_branch_name)
        })
        .collect::<Vec<_>>()
        .serialize(serializer)
}

fn remote_tracking_branch_parts(
    repo: &gix::Repository,
    branch: &gix::refs::FullNameRef,
) -> Result<(String, String)> {
    let (remote, short_name) = extract_remote_name_and_short_name(branch, &repo.remote_names())
        .ok_or_else(|| anyhow!("failed to determine remote and branch name for `{branch}`"))?;
    let short_name = std::str::from_utf8(short_name.as_ref())
        .context(format!("branch name for `{branch}` is not valid UTF-8"))?
        .to_owned();
    Ok((remote, short_name))
}

fn now_ms() -> u128 {
    UNIX_EPOCH
        .elapsed()
        .expect("system time is set before the Unix epoch")
        .as_millis()
}

async fn handle_git_prompt_push(
    prompt: String,
    askpass: Option<Option<StackId>>,
) -> Option<String> {
    if let Some(branch_id) = askpass {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Prune or delete the stale ref: `git remote prune <remote>` or `git update-ref -d refs/remotes/<stale-remote>/<branch>`
  2. Re-register the remote whose name prefixes the ref so the primary match succeeds: `git remote add <name> <url>`
  3. Filter inputs: only pass refs categorized as gix::refs::Category::RemoteBranch to remote-tracking helpers
  4. Check the ref spelling at the call site (refs/remotes/ vs refs/heads/)

Example fix

// before
let (remote, short_name) = remote_tracking_branch_parts(&repo, branch)?;

// after: pre-filter refs that the helper cannot resolve
let names = repo.remote_names();
match but_core::extract_remote_name_and_short_name(branch, &names) {
    Some((remote, short)) => (remote, short.to_string_lossy().into_owned()),
    None => {
        tracing::warn!("skipping unresolvable ref {}", branch.as_bstr());
        continue;
    }
}
Defensive patterns

Strategy: validation

Validate before calling

let names = repo.remote_names();
// but_core::extract_remote_name_and_short_name returns Option — use it as the pre-check
if but_core::extract_remote_name_and_short_name(branch, &names).is_none() {
    // not a resolvable remote-tracking ref: skip or report instead of calling in and failing
    tracing::warn!("unresolvable remote-tracking ref {}", branch.as_bstr());
    continue;
}
let (remote, short) = remote_tracking_branch_parts(&repo, branch)?;

Type guard

fn is_resolvable_rtb(r: &gix::refs::FullNameRef, remotes: &gix::remote::Names) -> bool {
    but_core::extract_remote_name_and_short_name(r, remotes).is_some()
}

Try / catch

match remote_tracking_branch_parts(&repo, branch) {
    Ok(parts) => parts,
    Err(err) if err.to_string().contains("failed to determine remote and branch name") => {
        tracing::warn!("stray remote-tracking ref {} — consider pruning", branch.as_bstr());
        continue;
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Passing a local branch ref (refs/heads/...) or a tag to a function that only accepts remote-tracking refs; a stray RTB like refs/remotes/gone/feature/sub after the remote 'gone' was removed, whose leftover short name 'feature/sub' contains a slash; remote_names from a different repo than the ref.

Common situations: Remote removed/renamed without pruning, leaving nested remote-tracking refs; refs created by tools that write under refs/remotes/ with multi-slash names; code iterating ALL references and feeding non-RTB refs into remote_tracking_branch_parts.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/2653e23cc9aeb076. Report an issue: GitHub.