gitbutlerapp/gitbutler · error

snapshot additional ref is not a local branch

Error message

snapshot additional ref is not a local branch

What it means

During snapshot restore, snapshot_reference reads 'additional-ref' entries saved in the snapshot tree. The ref's name blob must parse as a FullName and must be a local branch (Category::LocalBranch); only local branches are restored as additional refs. Anything else (tag, remote branch, malformed name) bails with this message.

Solutions

  1. Inspect the snapshot tree's additional-ref/name blob and correct it to a local branch name (refs/heads/...).
  2. Restore from an alternate snapshot that lacks the offending additional ref.
  3. Skip restoring the offending additional ref (manual re-creation of the branch afterwards).
  4. If produced by a stock GitButler snapshot, report as a snapshot-format bug with the snapshot id.

Example fix

// before (additional-ref/name blob in snapshot)
"refs/remotes/origin/feature"
// after
"refs/heads/feature"
Defensive patterns

Strategy: try-catch

Try / catch

match restore_snapshot(&ctx, id) {
    Err(e) if e.to_string().contains("additional ref is not a local branch") => {
        // restore without that ref, or pick a different snapshot
    }
    other => other?,
}

Prevention

When it happens

Trigger: restore_snapshot encountering an additional-ref whose stored name parses to a non-local-branch category, e.g. refs/remotes/origin/main, refs/tags/v1, or a fully malformed ref string that somehow parses to another category.

Common situations: Snapshots written by a version that stored other ref kinds as additional refs; manually edited snapshot trees; corrupted oplog data; custom tooling writing snapshot entries.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

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

}

struct SnapshotReference {
    ref_name: gix::refs::FullName,
    target: Option<gix::ObjectId>,
}

fn snapshot_reference(
    snapshot_tree: &gix::Tree<'_>,
    repo: &gix::Repository,
) -> Result<Option<SnapshotReference>> {
    let Some(name_entry) = snapshot_tree.lookup_entry_by_path("additional-ref/name")? else {
        return Ok(None);
    };
    let name_blob = repo.find_blob(name_entry.id())?;
    let ref_name = gix::refs::FullName::try_from(name_blob.data.as_bstr())
        .context("snapshot additional ref is invalid")?;
    if ref_name.category() != Some(gix::refs::Category::LocalBranch) {
        bail!("snapshot additional ref is not a local branch");
    }
    let target = snapshot_tree
        .lookup_entry_by_path("additional-ref/target")?
        .map(|entry| {
            let blob = repo.find_blob(entry.id())?;
            gix::ObjectId::from_hex(&blob.data).context("snapshot additional ref target is invalid")
        })
        .transpose()?;
    if target.is_some_and(|target| target.is_null()) {
        bail!("snapshot additional ref target is null");
    }
    Ok(Some(SnapshotReference { ref_name, target }))
}

fn snapshot_checkout(
    snapshot_tree: &gix::Tree<'_>,
    repo: &gix::Repository,
) -> Result<Option<SnapshotCheckout>> {

View on GitHub (pinned to 58e5313667)