gitbutlerapp/gitbutler · error
Visible worktree {worktree_name} HEAD changed shape during t
Error message
Visible worktree {worktree_name} HEAD changed shape during the edit: expected {}, got {} What it means
A companion check to the HEAD-drift guard while applying worktree checkouts: the editor compares the shape of HEAD — attached branch ref vs detached. If the worktree's HEAD switched between attached and detached (or to a differently-shaped ref) since the editor was created, it aborts rather than forcing a checkout onto a differently-shaped HEAD.
Source
Thrown at crates/but-rebase/src/graph_rebase/materialize.rs:151
/// one, validated against the shape recorded at editor creation.
pub(super) fn linked_checkout_specs(&self) -> Result<Vec<LinkedCheckoutSpec>> {
let mut specs = Vec::new();
for checkout in &self.checkouts {
let Checkout::Worktree {
worktree_name,
selector,
ref_name: expected_ref,
initial_head,
merge_base_override,
} = checkout
else {
continue;
};
let (target, actual_ref) = self
.checkout_target(*selector)?
.with_context(|| format!("Visible worktree {worktree_name} HEAD was removed"))?;
if actual_ref.as_ref() != expected_ref.as_ref() {
bail!(
"Visible worktree {worktree_name} HEAD changed shape during the edit: \
expected {}, got {}",
expected_ref
.as_ref()
.map_or_else(|| "detached".into(), ToString::to_string),
actual_ref
.as_ref()
.map_or_else(|| "detached".into(), ToString::to_string)
);
}
specs.push(LinkedCheckoutSpec {
name: worktree_name.clone(),
initial_head: *initial_head,
ref_name: expected_ref.clone(),
target,
merge_base_override: *merge_base_override,
});
}View on GitHub (pinned to caf1f223d3)
Solutions
- Recreate the editor session after HEAD shape stabilizes
- Avoid detaching or switching HEAD in linked worktrees while a graph edit is in progress
- If the detached state is intentional, rebuild the plan against the current (detached) HEAD
Example fix
// before
editor.materialize(...)?; // bails on attached/detached mismatch
// after: detect the shape change before materializing
let (actual_ref, _) = editor.checkout_target(*selector)?;
if actual_ref.as_ref() != expected_ref.as_ref() {
return rebuild_editor_and_retry(); // one is detached, the other is not
}
editor.materialize(...)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: detect attached/detached shape changes before materializing
let (actual_ref, _) = editor.checkout_target(*selector)?;
if actual_ref.as_ref() != expected_ref.as_ref() {
// one side is a branch ref, the other detached — rebuild the editor
return recreate_editor();
} Try / catch
Catch the bail, report which worktree changed shape (expected vs got), and rebuild the editor session from the current HEAD state rather than forcing the checkout.
Prevention
- Avoid detaching HEAD or switching branches in linked worktrees during pending edits
- Re-capture editor specs after any manual git operation in a linked worktree
When it happens
Trigger: During materialize, `checkout_target` reports a ref whose shape differs from the editor's expectation: the user ran `git checkout --detach` or `git switch <branch>` in the linked worktree mid-edit.
Common situations: Users experimenting in a linked worktree while an edit session is open; scripts detaching HEAD for inspection; tooling that checks out commits by id.
Related errors
- Visible worktree {} changed since the editor was created: ex
- Visible worktree {name} was listed more than once
- When using OpenRouter, you must provide a valid API key
- 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/668d6005e363d87e.
Report an issue: GitHub.