gitbutlerapp/gitbutler · error
snapshot additional ref target is null
Error message
snapshot additional ref target is null
What it means
This error is thrown by `snapshot_reference` when an additional reference recorded in a snapshot resolves to a blob whose contents are the all-zero object ID. GitButler stores extra refs (e.g. applied branch heads) inside snapshot trees as blobs containing a hex object id; a null id means the snapshot data is corrupt or was written from a repo that had no valid target. The library refuses to construct a `SnapshotReference` pointing at nothing.
Solutions
- Re-run `but oplog restore` on a different (earlier) snapshot entry that has valid ref targets
- Inspect the snapshot tree with `git ls-tree` on the oplog snapshot commit and check the blob contents for the affected ref; verify it is a valid 40-char hex sha
- Recreate the branch manually from a known commit and take a fresh snapshot instead of restoring the corrupt one
- If corruption is widespread, reset the oplog (`but oplog clear`) and continue with a clean history
Example fix
// corrupt blob: 0000000000000000000000000000000000000000 // fix: point the stored ref blob at a real commit // before: blob data = "0000000000000000000000000000000000000000" // after: blob data = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
Defensive patterns
Strategy: validation
Validate before calling
fn valid_snapshot_ref_target(repo: &gix::Repository, entry: &gix::tree::Entry) -> bool {
repo.find_blob(entry.id()).ok()
.and_then(|b| gix::ObjectId::from_hex(&b.data).ok())
.map(|id| !id.is_null())
.unwrap_or(false)
} Prevention
- Never hand-edit blobs inside oplog snapshot trees
- Check `but doctor`/logs for snapshot corruption warnings before restoring
- Keep multiple snapshot entries so a corrupt one can be skipped
- Avoid killing the app mid-snapshot-write; let snapshot operations complete
When it happens
Trigger: Restoring an oplog snapshot (`restore_snapshot` -> `snapshot_reference`) where a stored additional-ref blob contains hex '0000000000000000000000000000000000000000' (40 zeros).
Common situations: Corrupted or hand-edited snapshot data in `.git/gitbutler/` refs; snapshots produced by an older/buggy GitButler version that wrote zero ids; interrupted snapshot writes; manual git plumbing experiments that zeroed ref blobs.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- snapshot checkout commit is null
- cannot restore a snapshot without checkout identity outside…
- Cannot restore branch
- cannot snapshot a null target commit id
- Change to discard at
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/62ef047982a37cb9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/gitbutler-oplog/src/oplog.rs:568
) -> 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>> {
let Some(ref_entry) = snapshot_tree.lookup_entry_by_path("checkout/ref")? else {
return Ok(None);
};
let commit_entry = snapshot_tree
.lookup_entry_by_path("checkout/commit")?
.context("snapshot checkout ref has no commit")?;
let ref_blob = repo
.find_blob(ref_entry.id())
.context("failed to read snapshot checkout ref")?;
let ref_name = gix::refs::FullName::try_from(ref_blob.data.as_bstr())View on GitHub (pinned to 58e5313667)