{"record":{"id":"6e2376223d1ad0bb","repo":"gitbutlerapp/gitbutler","slug":"cannot-snapshot-a-null-target-commit-id","errorCode":null,"errorMessage":"cannot snapshot a null target commit id","messagePattern":"cannot snapshot a null target commit id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-oplog/src/oplog.rs","lineNumber":76,"sourceCode":"    virtual_branches: VirtualBranches,\n}\n\n#[derive(serde::Deserialize)]\n#[serde(rename_all = \"camelCase\")]\nstruct SnapshotTarget {\n    branch_name: String,\n    remote_name: String,\n    sha: String,\n    push_remote_name: Option<String>,\n}\n\nimpl TryFrom<&ProjectMeta> for SnapshotProjectMeta {\n    type Error = anyhow::Error;\n\n    fn try_from(meta: &ProjectMeta) -> Result<Self> {\n        let target_commit_id = meta.target_commit_id_or_err()?;\n        if target_commit_id.is_null() {\n            bail!(\"cannot snapshot a null target commit id\");\n        }\n        Ok(Self {\n            target_ref: meta.target_ref.as_ref().map(ToString::to_string),\n            target_commit_id: target_commit_id.to_string(),\n            push_remote: meta.push_remote.clone(),\n        })\n    }\n}\n\nimpl TryFrom<SnapshotProjectMeta> for ProjectMeta {\n    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()","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-oplog/src/oplog.rs#L58-L94","documentation":"When the oplog snapshots a project, `ProjectMeta` is converted into `SnapshotProjectMeta`; the project's target commit id must exist and must not be the null object id. A null target means the project has no target branch commit assigned, so the snapshot is refused rather than persisting meaningless metadata.","triggerScenarios":"Creating an oplog snapshot (every user-facing mutation records one) for a project whose `target_commit_id` is null — e.g. a project added without selecting a target branch, or whose target assignment failed.","commonSituations":"Freshly onboarded projects where target branch selection was skipped; project metadata restored from backup or edited by hand missing the target; a stale project cache hiding the missing target.","solutions":["Set or reselect the project's target branch so `target_commit_id` is populated, then retry","Verify project metadata: `target_commit_id_or_err()` must return a non-null oid before snapshotting","If the target is set in the UI yet still null, reload the project / clear the cache and retry"],"exampleFix":"// before: snapshot attempted with no target\noplog.create_snapshot(&ctx, ...)?; // bail: cannot snapshot a null target commit id\n\n// after: assign a target branch first\nset_target_branch(&ctx, \"origin/main\")?;\noplog.create_snapshot(&ctx, ...)?;","handlingStrategy":"validation","validationCode":"let meta = current_project_meta(&ctx)?;\nlet target = meta.target_commit_id_or_err()?;\nif target.is_null() {\n    return Err(anyhow::anyhow!(\"project has no target commit; pick a target branch first\"));\n}","typeGuard":"fn has_snapshot_ready_target(meta: &ProjectMeta) -> bool {\n    meta.target_commit_id.is_some_and(|id| !id.is_null())\n}","tryCatchPattern":"match oplog.create_snapshot(&ctx, /* ... */) {\n    Err(err) if err.to_string().contains(\"null target commit id\") => { /* prompt to select a target branch */ }\n    other => other,\n}","preventionTips":["Require target branch selection during project onboarding","Validate ProjectMeta before starting undoable mutations"],"tags":["rust","gitbutler","oplog","snapshot","target-branch"],"backgroundTag":"missing-target-branch","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}