{"record":{"id":"e5d24f06d440825d","repo":"gitbutlerapp/gitbutler","slug":"return-anyhow-error-new-explainedrejection-format-cannot","errorCode":null,"errorMessage":"return anyhow::Error::new(ExplainedRejection(format!(\"Cannot {verb}: {rejected}\")))","messagePattern":"return anyhow::Error::new\\(ExplainedRejection\\(format!\\(\"Cannot (.+?): (.+?)\"\\)\\)\\)","errorType":"error_code","errorClass":"ExplainedRejection","httpStatus":null,"severity":"warning","filePath":"crates/but/src/utils/rejection.rs","lineNumber":100,"sourceCode":"pub fn explain_after_rollback(\n    ctx: &Context,\n    perm: &mut RepoExclusive,\n    verb: &str,\n    target: Target,\n    err: anyhow::Error,\n) -> anyhow::Error {\n    let rejected = match err.downcast::<RejectedChanges>() {\n        Ok(rejected) => rejected,\n        Err(other) => return other,\n    };\n\n    // Explaining a failure must never mask it: without a workspace projection,\n    // fall back to the plain rejection count.\n    let (repo, ws, _db) = match ctx.workspace_and_db_with_perm(perm.read_permission()) {\n        Ok(loaded) => loaded,\n        Err(load_err) => {\n            tracing::warn!(?load_err, \"Failed to load workspace to explain rejections\");\n            return anyhow::Error::new(ExplainedRejection(format!(\"Cannot {verb}: {rejected}\")));\n        }\n    };\n    let (repo, ws) = (&repo, &ws);\n\n    let target_branch = match &target {\n        Target::Commit(commit) => branch_of_commit(ws, commit.commit_id, None),\n        Target::Branch(name) => Some(name.clone()),\n        Target::NewBranch(_) => None,\n    };\n\n    let changes = match explain_rejections(repo, ws, &rejected.0, target_branch.as_deref()) {\n        Ok(changes) => changes,\n        Err(other) => return other,\n    };\n\n    let mut message = format!(\"Cannot {verb}: {rejected}:\\n\");\n    // Writing to a String cannot fail.\n    let _ = write_report(&mut message, &changes, &target, target_branch.as_deref());","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but/src/utils/rejection.rs#L82-L118","documentation":"When explaining why changes were rejected (`explain_after_rollback`), the workspace and database must be loaded to produce a dependency report. If that load fails, the function falls back to a minimal `ExplainedRejection` containing only the verb and rejection count — the explanation is degraded but the original failure is never masked.","triggerScenarios":"Calling the post-rollback explanation path when `ctx.workspace_and_db_with_perm` fails, e.g. the workspace state on disk is corrupt/locked, the database cannot be opened, or the read permission is unavailable at that moment.","commonSituations":"A crashed prior run left the workspace data in a bad state; the project database file is locked by another process; repository moved/deleted between the operation and the explanation; permission degradation during shutdown.","solutions":["Treat the message as a count-only explanation and inspect the earlier root-cause error alongside it.","Check for and clear stale locks on the project database / workspace files.","Re-open or re-initialize the project in GitButler to rebuild the workspace state.","Enable tracing output — the load failure is logged with `tracing::warn!(?load_err, ...)`; use that error to fix the underlying cause."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// consume the ExplainedRejection but keep the degraded message\nmatch op().await {\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.starts_with(\"Cannot \") { log::warn!(\"explanation degraded: {msg}\"); }\n        Err(e)\n    }\n    ok => ok,\n}","preventionTips":["Avoid running multiple processes against the same project database.","Ensure clean shutdown so workspace state is never left half-written.","Check tracing warnings ('Failed to load workspace to explain rejections') for the root cause."],"tags":["workspace","rejection","internal","fallback"],"backgroundTag":"internal-invariant-violation","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}