gitbutlerapp/gitbutler · error

targetCommitId in project_meta.toml is null

Error message

targetCommitId in project_meta.toml is null

What it means

The target commit id stored in a snapshot's `project_meta.toml` parsed as a valid hex object id but equals the null oid. Since a null target is meaningless, restore refuses; the field was written with placeholder or corrupt data.

Source

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

    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.
/// Snapshots include the state of the working directory as well as all additional GitButler state (e.g. virtual branches, conflict state).
/// The data is stored as git trees in the following shape:
///
/// ```text
/// .
/// ├── checkout/ (ad-hoc checkouts only)
/// │   ├── commit
/// │   └── ref

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Restore an earlier snapshot that records a real target commit id
  2. If the snapshot must be used, repair its metadata tree to record the intended target commit
  3. Report a bug if the snapshot was produced by the currently running version
Defensive patterns

Strategy: validation

Validate before calling

let id = gix::ObjectId::from_str(&meta.target_commit_id)?;
if id.is_null() {
    return Err(anyhow::anyhow!("snapshot has a null targetCommitId; refusing restore"));
}

Type guard

fn has_valid_target_commit(meta: &SnapshotProjectMeta) -> bool {
    gix::ObjectId::from_str(&meta.target_commit_id).is_ok_and(|id| !id.is_null())
}

Try / catch

match oplog.restore(snapshot_id) {
    Err(err) if err.to_string().contains("targetCommitId") => { /* choose an earlier snapshot */ }
    other => other,
}

Prevention

When it happens

Trigger: Restoring a snapshot whose `targetCommitId` field is all zeros or an explicit null placeholder.

Common situations: Snapshots written from default/empty ProjectMeta values; truncated or hand-crafted snapshot trees; bugs in older snapshot writers.

Related errors


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