gitbutlerapp/gitbutler · error

Commit is not a snapshot

Error message

Commit is not a snapshot

What it means

OplogExt::get_snapshot (crates/gitbutler-oplog/src/oplog.rs) treats a commit as an oplog snapshot only if its raw commit message parses into SnapshotDetails (via SnapshotDetails::from_str). If the message is not valid UTF-8 or does not parse, the commit is rejected with this error. Not every commit in the repository is an oplog entry — only those created by the oplog with serialized SnapshotDetails in the message.

Source

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

    ) -> Result<gix::ObjectId> {
        let PreparedSnapshot {
            tree_id,
            target_base_oid,
        } = prepare_snapshot_with_target(self, perm.read_permission())?;
        let repo = self.repo.get()?;
        commit_snapshot(self, &repo, tree_id, details, perm, target_base_oid)
    }

    #[instrument(skip(self), err(Debug))]
    fn get_snapshot(&self, sha: gix::ObjectId) -> Result<Snapshot> {
        let repo = self.repo.get()?;
        let commit = repo.find_commit(sha)?;
        let details = commit
            .message_raw()?
            .to_str()
            .ok()
            .and_then(|msg| SnapshotDetails::from_str(msg).ok())
            .ok_or(anyhow!("Commit is not a snapshot"))?;

        let snapshot = Snapshot {
            commit_id: sha,
            created_at: commit.time()?,
            details: Some(details),
        };
        Ok(snapshot)
    }

    #[instrument(skip(self), err(Debug))]
    fn snapshots_iter(
        &self,
        oplog_commit_id: Option<gix::ObjectId>,
        exclude_kind: Vec<OperationKind>,
        include_kind: Option<Vec<OperationKind>>,
    ) -> Result<impl Iterator<Item = Result<Snapshot>>> {
        let repo = self.repo.get()?.clone();
        let next_commit_id = match oplog_commit_id {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Only pass shas that come from the oplog itself: oplog_head() or snapshots_iter() entries
  2. If integrating by sha from external input, validate it first by walking snapshots_iter and matching the id
  3. If snapshots from an old version fail to parse, update the app — parsers keep compatibility for known formats
  4. Inspect the commit directly (`git cat-file commit <sha>`) to confirm whether the message really lacks SnapshotDetails

Example fix

// before
let snap = ctx.get_snapshot(user_provided_sha)?; // user commit -> 'Commit is not a snapshot'

// after: only use shas produced by the oplog
let Some(head) = ctx.oplog_head()? else {
    return Ok(());
};
let snap = ctx.get_snapshot(head)?;
Defensive patterns

Strategy: validation

Validate before calling

// only accept shas the oplog itself produced
let valid: HashSet<gix::ObjectId> = ctx
    .snapshots_iter(None, vec![], None)?
    .filter_map(|s| s.ok().map(|s| s.commit_id))
    .collect();
if !valid.contains(&requested_sha) {
    anyhow::bail!("{requested_sha} is not an oplog snapshot");
}
let snap = ctx.get_snapshot(requested_sha)?;

Type guard

fn is_oplog_snapshot(ctx: &Context, sha: gix::ObjectId) -> bool {
    ctx.snapshots_iter(None, vec![], None)
        .map(|mut it| it.any(|s| s.ok().is_some_and(|s| s.commit_id == sha)))
        .unwrap_or(false)
}

Try / catch

match ctx.get_snapshot(sha) {
    Ok(snap) => Ok(Some(snap)),
    Err(err) if err.to_string().contains("Commit is not a snapshot") => Ok(None), // caller passed a non-oplog commit
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: Calling get_snapshot(sha) with an arbitrary workspace/user commit sha instead of one produced by the oplog; passing a sha obtained from user branches, gitbutler/workspace, or stash-like commits; an oplog commit written by a much older version whose message format no longer parses; a commit whose message was rewritten externally.

Common situations: Frontend or SDK code feeding a branch head sha where a snapshot sha is expected; deserialization drift after format changes between app versions; sha read from a stale settings/URL parameter; restoring by sha copied from an old timeline entry after a format change.

Related errors


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