gitbutlerapp/gitbutler · error
Remote HEAD for is not symbolic
Error message
Remote HEAD for {remote_name} is not symbolic What it means
refs/remotes/<remote>/HEAD must be a symbolic reference pointing at the remote's default branch (e.g. refs/remotes/origin/main). default_target_setting_if_none calls try_name() on the ref's target and throws this error if the ref exists but is not symbolic — i.e. it points directly at an object ID or holds an unexpected target kind — so the default branch name cannot be extracted.
Solutions
- Recreate it as a symbolic ref: `git remote set-head origin --auto` (or `--set` to a branch).
- Alternatively update the ref directly: `git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main`.
- Configure the GitButler target branch explicitly so the symbolic remote HEAD is not needed.
Example fix
// before: HEAD is detached at an OID $ git rev-parse refs/remotes/origin/HEAD abc123... // after $ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
Defensive patterns
Strategy: validation
Validate before calling
let is_sym = gix::refs::Reference::lookup("refs/remotes/origin/HEAD", repo)
.map(|r| r.target().try_name().is_some())
.unwrap_or(false);
if !is_sym {
// repair: git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
} Try / catch
match default_target_setting_if_none(ctx) {
Err(e) if e.to_string().contains("is not symbolic") => {
// recreate the symref via git symbolic-ref / git remote set-head, then retry
}
other => other?,
} Prevention
- Never rewrite refs/remotes/<remote>/HEAD as a detached OID; keep it symbolic.
- Use `git remote set-head` rather than manual ref writes to manage remote HEAD.
- Audit tooling that edits refs so it preserves symref targets.
When it happens
Trigger: The target of refs/remotes/{remote}/HEAD is not a symbolic name: the ref was written as a direct OID (by a tool or manual refs update), or ref storage returned a non-name target (e.g. a peeled object id).
Common situations: Third-party tools or scripts that rewrote remote HEAD as a detached ref; manually edited packed-refs; unusual repos migrated from other systems where HEAD symrefs were flattened.
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
- Attempted to update the symbolic reference
- No HEAD reference found for remote
- Can't handle reference
- Cannot currently handle remotes as start position
- DefaultTargetNotFound
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/8a5b4832d128a246.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-action/src/lib.rs:158
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")]
pub enum ActionHandler {
#[default]
HandleChangesSimple,
}View on GitHub (pinned to 58e5313667)