gitbutlerapp/gitbutler · error

No HEAD reference found for remote {remote_name}

Error message

No HEAD reference found for remote {remote_name}

What it means

While guessing the default target branch, GitButler looks up refs/remotes/<remote>/HEAD (the symref `git remote set-head` creates). If that reference does not exist, find_reference fails and default_target_setting_if_none aborts with this message naming the remote. The remote itself is configured; only its HEAD shortcut is missing.

Source

Thrown at crates/but-action/src/lib.rs:154

        }
    }
}

fn default_target_setting_if_none(ctx: &Context) -> anyhow::Result<()> {
    if ctx.project_meta()?.target_ref.is_some() {
        return Ok(());
    }
    // Lets do the equivalent of `git symbolic-ref refs/remotes/origin/HEAD --short` to guess the default target.

    let repo = ctx.repo.get()?;
    let remote_name = repo
        .remote_default_name(gix::remote::Direction::Push)
        .ok_or_else(|| anyhow::anyhow!("No push remote set or more than one remote"))?
        .to_string();

    let mut head_ref = repo
        .find_reference(&format!("refs/remotes/{remote_name}/HEAD"))
        .map_err(|_| anyhow::anyhow!("No HEAD reference found for remote {remote_name}"))?;
    let target_ref_name = head_ref
        .target()
        .try_name()
        .ok_or_else(|| anyhow::anyhow!("Remote HEAD for {remote_name} is not symbolic"))?
        .to_owned();

    let head_commit = head_ref.peel_to_commit()?;

    ctx.set_project_meta(ProjectMeta {
        target_ref: Some(target_ref_name),
        target_commit_id: Some(head_commit.id),
        push_remote: None,
    })?;
    Ok(())
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, EnumString, Default)]
#[serde(rename_all = "camelCase")]

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Run git remote set-head <remote> --auto to (re)create refs/remotes/<remote>/HEAD.
  2. If --auto cannot query the remote, set it explicitly: git remote set-head <remote> main.
  3. Or set the target branch directly in GitButler so the guess path never runs.
  4. Verify with git symbolic-ref refs/remotes/<remote>/HEAD.

Example fix

git remote set-head origin --auto
git symbolic-ref refs/remotes/origin/HEAD  # should print refs/remotes/origin/<branch>
# then retry the GitButler action
Defensive patterns

Strategy: validation

Validate before calling

fn remote_head_exists(repo: &gix::Repository, remote: &str) -> bool {
    repo.find_reference(&format!("refs/remotes/{remote}/HEAD")).is_ok()
}

// before the first GitButler action on a repo:
if !remote_head_exists(repo, &remote_name) {
    std::process::Command::new("git")
        .args(["remote", "set-head", &remote_name, "--auto"])
        .status()?;
}

Try / catch

match default_target_setting_if_none(ctx) {
    Err(err) if err.to_string().contains("No HEAD reference found for remote") => {
        run_git(&["remote", "set-head", &remote, "--auto"]); // then retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: default_target_setting_if_none on a repo whose push remote has no refs/remotes/<remote>/HEAD: remotes added with `git remote add` but never given a HEAD, shallow or --single-branch clones, repos materialized by scripts that skip the symref.

Common situations: git clone --single-branch / --depth clones; tooling and containers that construct remotes manually; older Git clients that did not write remote HEAD; mirror setups.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/bd4711c54fdeb8df. Report an issue: GitHub.