gitbutlerapp/gitbutler · error

targetRef in project_meta.toml is not a remote-tracking bran

Error message

targetRef in project_meta.toml is not a remote-tracking branch

What it means

On snapshot restore, `ProjectMeta` is rebuilt from the stored `project_meta.toml`; a `targetRef` must be a remote-tracking branch (a ref under `refs/remotes/`). A ref name in any other category — local branch, tag, or other — is rejected so a bogus target can never be restored into the project.

Source

Thrown at crates/gitbutler-oplog/src/oplog.rs:97

            target_ref: meta.target_ref.as_ref().map(ToString::to_string),
            target_commit_id: target_commit_id.to_string(),
            push_remote: meta.push_remote.clone(),
        })
    }
}

impl TryFrom<SnapshotProjectMeta> for ProjectMeta {
    type Error = anyhow::Error;

    fn try_from(meta: SnapshotProjectMeta) -> Result<Self> {
        let target_ref = meta
            .target_ref
            .map(|name| {
                let name: gix::refs::FullName = name
                    .try_into()
                    .context("invalid targetRef in project_meta.toml")?;
                if name.category() != Some(gix::refs::Category::RemoteBranch) {
                    bail!("targetRef in project_meta.toml is not a remote-tracking branch");
                }
                Ok(name)
            })
            .transpose()?;
        let target_commit_id = gix::ObjectId::from_str(&meta.target_commit_id)
            .context("invalid targetCommitId in project_meta.toml")?;
        if target_commit_id.is_null() {
            bail!("targetCommitId in project_meta.toml is null");
        }
        Ok(Self {
            target_ref,
            target_commit_id: Some(target_commit_id),
            push_remote: meta.push_remote,
        })
    }
}

/// The Oplog allows for crating snapshots of the current state of the project as well as restoring to a previous snapshot.

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Fix the snapshot's targetRef to a valid remote-tracking ref such as `refs/remotes/origin/main` and retry the restore
  2. Restore from an earlier, consistent snapshot entry instead
  3. Recreate the state manually (reselect the target branch, re-apply changes) if the snapshot cannot be repaired
Defensive patterns

Strategy: validation

Validate before calling

let name: gix::refs::FullName = stored_target_ref.parse()?;
if name.category() != Some(gix::refs::Category::RemoteBranch) {
    return Err(anyhow::anyhow!("refusing snapshot with non-remote-tracking targetRef {name}"));
}

Type guard

fn is_remote_tracking(ref_name: &gix::refs::FullName) -> bool {
    ref_name.category() == Some(gix::refs::Category::RemoteBranch)
}

Try / catch

match oplog.restore(snapshot_id) {
    Err(err) if err.to_string().contains("not a remote-tracking branch") => { /* pick another snapshot */ }
    other => other,
}

Prevention

When it happens

Trigger: Restoring an oplog snapshot whose recorded targetRef is not `refs/remotes/<remote>/<branch>`, e.g. `refs/heads/main` or `refs/tags/v1` stored in the snapshot's metadata tree.

Common situations: Hand-edited snapshot trees; snapshots written by an incompatible GitButler version or external tooling; migration artifacts from older formats.

Related errors


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