gitbutlerapp/gitbutler · error
Visible worktree {} changed since the editor was created: ex
Error message
Visible worktree {} changed since the editor was created: expected {} at {}, got {} at {} What it means
Before materializing a graph edit, the editor re-reads each linked worktree's HEAD and compares ref name and commit id against the values captured when the editor was created. Any drift (branch moved, different commit, attached vs detached) aborts with expected-vs-actual details, preventing the edit from clobbering concurrent changes.
Source
Thrown at crates/but-rebase/src/graph_rebase/materialize.rs:97
repo: &gix::Repository,
specs: Vec<LinkedCheckoutSpec>,
) -> Result<Vec<LinkedCheckoutRepo>> {
if specs.is_empty() {
return Ok(Vec::new());
}
let proxies = repo.worktrees()?;
specs
.into_iter()
.map(|spec| {
let proxy = proxies
.iter()
.find(|proxy| proxy.id() == spec.name.as_bstr())
.with_context(|| format!("Visible worktree {} no longer exists", spec.name))?;
let worktree_repo = proxy.clone().into_repo()?;
let actual_ref = worktree_repo.head_name()?;
let actual_head = worktree_repo.head_id()?.detach();
if actual_ref.as_ref() != spec.ref_name.as_ref() || actual_head != spec.initial_head {
bail!(
"Visible worktree {} changed since the editor was created: \
expected {} at {}, got {} at {}",
spec.name,
spec.ref_name
.as_ref()
.map_or_else(|| "detached".into(), ToString::to_string),
spec.initial_head,
actual_ref
.as_ref()
.map_or_else(|| "detached".into(), ToString::to_string),
actual_head
);
}
Ok(LinkedCheckoutRepo {
repo: worktree_repo,
target: spec.target,
merge_base_override: spec.merge_base_override,
})View on GitHub (pinned to caf1f223d3)
Solutions
- Re-create the editor (re-capture the worktree specs) and retry once the worktrees are idle
- Finish or close other sessions/processes touching the worktree, then retry
- Verify each worktree's current HEAD matches expectations before re-applying
Example fix
// before
editor.materialize(...)?; // bails if any worktree HEAD drifted
// after: re-verify specs first and rebuild on drift
for spec in &specs {
let (actual_ref, actual_head) = read_worktree_head(&repo, &spec.name)?;
if actual_ref.as_ref() != spec.ref_name.as_ref() || actual_head != spec.initial_head {
return rebuild_editor_and_retry();
}
}
editor.materialize(...)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: re-verify captured specs against live worktree HEADs before materializing
for spec in &specs {
let wt = worktree_repo(&repo, &spec.name)?;
let actual_ref = wt.head_name()?;
let actual_head = wt.head_id()?.detach();
if actual_ref.as_ref() != spec.ref_name.as_ref() || actual_head != spec.initial_head {
return recreate_editor(); // HEAD drifted since capture
}
} Try / catch
On this error, do not force-apply: surface expected vs actual to the user, offer to recreate the editor from the current state, and retry once after the worktree is idle.
Prevention
- Keep edit sessions short; materialize soon after capturing specs
- Serialize access: discourage commits/checkouts in linked worktrees while an edit is pending
When it happens
Trigger: Materializing an edit after someone committed, checked out, rebased, or moved HEAD in a linked worktree while the editor session was open; or another process (git CLI, another app window) updated the ref.
Common situations: Long-lived interactive edit sessions; scripts or CI touching the same worktree concurrently; two windows of the app open on one repository.
Related errors
- Visible worktree {worktree_name} HEAD changed shape during t
- Visible worktree {name} was listed more than once
- Failed to find commit {target} in rebase editor
- Invalid token format
- Login token expired. Please log in to GitButler again.
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/ed7bd745c2b75f03.
Report an issue: GitHub.