{"record":{"id":"23b59ada9497410d","repo":"gitbutlerapp/gitbutler","slug":"targetcommitid-in-project-meta-toml-is-null","errorCode":null,"errorMessage":"targetCommitId in project_meta.toml is null","messagePattern":"targetCommitId in project_meta\\.toml is null","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-oplog/src/oplog.rs","lineNumber":105,"sourceCode":"    type Error = anyhow::Error;\n\n    fn try_from(meta: SnapshotProjectMeta) -> Result<Self> {\n        let target_ref = meta\n            .target_ref\n            .map(|name| {\n                let name: gix::refs::FullName = name\n                    .try_into()\n                    .context(\"invalid targetRef in project_meta.toml\")?;\n                if name.category() != Some(gix::refs::Category::RemoteBranch) {\n                    bail!(\"targetRef in project_meta.toml is not a remote-tracking branch\");\n                }\n                Ok(name)\n            })\n            .transpose()?;\n        let target_commit_id = gix::ObjectId::from_str(&meta.target_commit_id)\n            .context(\"invalid targetCommitId in project_meta.toml\")?;\n        if target_commit_id.is_null() {\n            bail!(\"targetCommitId in project_meta.toml is null\");\n        }\n        Ok(Self {\n            target_ref,\n            target_commit_id: Some(target_commit_id),\n            push_remote: meta.push_remote,\n        })\n    }\n}\n\n/// The Oplog allows for crating snapshots of the current state of the project as well as restoring to a previous snapshot.\n/// Snapshots include the state of the working directory as well as all additional GitButler state (e.g. virtual branches, conflict state).\n/// The data is stored as git trees in the following shape:\n///\n/// ```text\n/// .\n/// ├── checkout/ (ad-hoc checkouts only)\n/// │   ├── commit\n/// │   └── ref","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-oplog/src/oplog.rs#L87-L123","documentation":"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.","triggerScenarios":"Restoring a snapshot whose `targetCommitId` field is all zeros or an explicit null placeholder.","commonSituations":"Snapshots written from default/empty ProjectMeta values; truncated or hand-crafted snapshot trees; bugs in older snapshot writers.","solutions":["Restore an earlier snapshot that records a real target commit id","If the snapshot must be used, repair its metadata tree to record the intended target commit","Report a bug if the snapshot was produced by the currently running version"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let id = gix::ObjectId::from_str(&meta.target_commit_id)?;\nif id.is_null() {\n    return Err(anyhow::anyhow!(\"snapshot has a null targetCommitId; refusing restore\"));\n}","typeGuard":"fn has_valid_target_commit(meta: &SnapshotProjectMeta) -> bool {\n    gix::ObjectId::from_str(&meta.target_commit_id).is_ok_and(|id| !id.is_null())\n}","tryCatchPattern":"match oplog.restore(snapshot_id) {\n    Err(err) if err.to_string().contains(\"targetCommitId\") => { /* choose an earlier snapshot */ }\n    other => other,\n}","preventionTips":["Reject null object ids when serializing snapshot metadata","Keep oplog snapshots intact; never edit snapshot trees by hand"],"tags":["rust","gitbutler","oplog","restore","null-oid","metadata"],"backgroundTag":"snapshot-metadata-invalid","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}