gitbutlerapp/gitbutler · error

No HEAD reference found for remote {remote_name}

Error message

No HEAD reference found for remote {remote_name}

What it means

Thrown during default-target detection after a push remote was found but refs/remotes/<name>/HEAD does not exist. The code resolves that symbolic ref to learn the remote's default branch (like git symbolic-ref refs/remotes/origin/HEAD); find_reference fails when the ref was never written. A missing remote HEAD typically means no fetch has populated it yet or the ref was deleted.

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 caf1f223d3)

Solutions

  1. Run git remote set-head <remote> --auto (or simply git fetch <remote>) to create the symbolic HEAD, then retry
  2. If the remote repository is empty, push or fetch a default branch first so a HEAD exists
  3. Alternatively set the project's target branch explicitly to skip detection

Example fix

# before: refs/remotes/origin/HEAD missing -> "No HEAD reference found for remote origin"
# after: recreate it, then retry
git remote set-head origin --auto
Defensive patterns

Strategy: validation

Validate before calling

let head_ref = format!("refs/remotes/{remote_name}/HEAD");
if repo.find_reference(&head_ref).is_err() {
    // populate it first: `git remote set-head <remote> --auto` or `git fetch <remote>`
}

Prevention

When it happens

Trigger: default_target_setting_if_none with target_ref unset: remote_default_name(Push) returns a name, but repo.find_reference("refs/remotes/<name>/HEAD") errs - common after adding a remote without fetching, or when the ref was pruned or never created by the clone tooling.

Common situations: git remote add without a following git fetch; shallow/partial clones missing the HEAD symref; repos where refs/remotes/origin/HEAD was deleted manually or by aggressive ref pruning; freshly mirrored repositories.

Related errors


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