gitbutlerapp/gitbutler · error

No HEAD reference found for remote

Error message

No HEAD reference found for remote {remote_name}

What it means

After determining the default push remote, default_target_setting_if_none looks up refs/remotes/<remote>/HEAD, which encodes the remote's default branch. If that symbolic reference does not exist in the local repo, this error is thrown, because without it GitButler cannot guess which branch should be the workspace target.

Solutions

  1. Recreate the remote HEAD: `git remote set-head origin --auto` (queries the remote for its default branch), then retry.
  2. Or set it manually: `git remote set-head origin main` (or the actual default branch).
  3. Run a full fetch (`git fetch origin`) to populate remote-tracking refs if the clone is single-branch.
  4. Configure the GitButler target branch explicitly to skip the guesswork.

Example fix

// before
$ git branch -r
  origin/main   # but no origin/HEAD

// after
$ git remote set-head origin --auto
$ retry operation
Defensive patterns

Strategy: validation

Validate before calling

if repo.find_reference("refs/remotes/origin/HEAD").is_err() {
    // repair before calling the API:
    // git remote set-head origin --auto
}

Try / catch

match default_target_setting_if_none(ctx) {
    Err(e) if e.to_string().contains("No HEAD reference found") => {
        // run `git remote set-head <remote> --auto`, then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: default_target_setting_if_none calls repo.find_reference("refs/remotes/{remote_name}/HEAD") and the ref is absent — the clone was made with a git version/setting that skips remote HEAD, or no fetch has populated remote-tracking refs yet.

Common situations: Shallow or partial clones missing remote-tracking refs; repos cloned with remote.origin.fetch configured narrowly (single-branch clone) so refs/remotes/origin/HEAD was never created; a bare/mirrored setup copied without the HEAD symref.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/bd4711c54fdeb8df. Report an issue: GitHub.

Appendix: 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 58e5313667)