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
- Inspect the snapshot tree's additional-ref/name blob and correct it to a local branch name (refs/heads/...).
- Restore from an alternate snapshot that lacks the offending additional ref.
- Skip restoring the offending additional ref (manual re-creation of the branch afterwards).
- 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
- Only record local branches as additional refs in snapshots
- Validate ref category when writing snapshot additional-ref entries
- Keep snapshot format changes backward-compatible across versions
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
- targetRef in project_meta.toml is not a remote-tracking…
- Invalid conflict stage
- targetCommitId in project_meta.toml is null
- Aborting due to empty branch name
- Branch name ' ' collides with existing branch
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)