gitbutlerapp/gitbutler · error
The working tree differs from the original commit. A forced
Error message
The working tree differs from the original commit. A forced abort is necessary.\nIf you are seeing this message, please report it as a bug. The UI should have prevented this line getting hit.
What it means
Aborting edit mode via `abort_and_return_to_workspace` refuses to proceed without `force` when the working tree still differs from the commit edit mode started from (`changes_from_initial` returns entries). The GitButler UI is supposed to detect this divergence and get user confirmation before forcing, so the message itself says hitting it is a bug in whatever called the API unguarded.
Source
Thrown at crates/gitbutler-edit-mode/src/lib.rs:281
for ref_name in [EDIT_BRANCH_REF, UNCOMMITTED_CHANGES_REF] {
// Tolerate an already-absent ref, but surface real lookup failures.
if let Some(reference) = repo.try_find_reference(ref_name)? {
reference
.delete()
.with_context(|| format!("Failed to delete reference {ref_name}"))?;
}
}
delete_edit_mode_metadata(ctx)?;
Ok(())
}
pub(crate) fn abort_and_return_to_workspace(
ctx: &Context,
force: bool,
perm: &mut RepoExclusive,
) -> Result<()> {
if !force && !changes_from_initial(ctx, perm.read_permission())?.is_empty() {
bail!(
"The working tree differs from the original commit. A forced abort is necessary.\nIf you are seeing this message, please report it as a bug. The UI should have prevented this line getting hit."
);
}
#[expect(deprecated, reason = "checkout/materialization boundary")]
let repo = &*ctx.git2_repo.get()?;
// Checkout gitbutler workspace branch
repo.set_head(WORKSPACE_BRANCH_REF)
.context("Failed to set head reference")?;
let uncommited_changes = get_uncommitted_changes(&*ctx.repo.get()?)?;
let uncommited_changes = repo.find_tree(uncommited_changes.to_git2())?;
repo.checkout_tree(
uncommited_changes.as_object(),
Some(CheckoutBuilder::new().force().remove_untracked(true)),
)?;View on GitHub (pinned to caf1f223d3)
Solutions
- If discarding the divergence is intended, pass `force: true` — exactly what the UI does after user confirmation
- Otherwise commit or revert the working-tree changes first, then abort edit mode without force
- If reached through the GitButler app UI, report it as a bug — the UI should have prevented it
Example fix
// before abort_and_return_to_workspace(&ctx, /* force */ false, &mut perm)?; // after: caller decides, like the UI does let force = user_confirmed_discard(); abort_and_return_to_workspace(&ctx, force, &mut perm)?;
Defensive patterns
Strategy: validation
Validate before calling
// ask the edit-mode status API whether the worktree diverged before aborting
if edit_mode_reports_changes(&ctx)? && !user_confirmed_discard {
return Err(anyhow::anyhow!("working tree diverged; confirm discard to force abort"));
} Try / catch
match abort_and_return_to_workspace(&ctx, force, &mut perm) {
Err(err) if err.to_string().contains("forced abort is necessary") => {
// prompt the user, then retry with force = true
}
other => other,
} Prevention
- Always show a confirmation step when aborting edit mode
- Check edit-mode divergence status before calling abort in scripts
When it happens
Trigger: Calling `abort_and_return_to_workspace(ctx, force = false, ...)` while the working tree has modifications relative to the original edit-mode commit — edits made during edit mode that were never committed or reverted.
Common situations: Scripts or tests calling the edit-mode abort API directly; races where UI state says clean but the worktree is dirty; programmatic operating-mode switching that skips the confirmation step.
Related errors
- Starting index state can only be fetched while in edit mode
- Failed to communicate with LM Studio server: ${error instanc
- Invalid response: ${JSON.stringify(result)}
- When using GitButler's API to summarize code, you must be lo
- When using LM Studio, you must provide a valid endpoint
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/37ffcdd4d960326f.
Report an issue: GitHub.