gastownhall/beads · error

worktree HEAD is not contained in the comparison target

Error message

worktree HEAD is not contained in the comparison target

What it means

Prepare rejects the request because mode is Normal and facts.Containment is NotContained: the worktree HEAD is not contained in the comparison target, meaning there are commits on the worktree branch that have not been pushed/merged. Normal-mode removal refuses to orphan those commits.

Source

Thrown at internal/worktreeremove/policy.go:214

	}
	if (facts.ManagedIgnore == IgnoreManaged) != (facts.ManagedIgnoreEntry != "") {
		return Plan{}, fmt.Errorf("managed ignore cleanup identity is absent or invalid")
	}
	if request.Mode != Force {
		if facts.Status != Clean {
			return Plan{}, fmt.Errorf("worktree contains modified, untracked, or ignored files")
		}
		if facts.Comparator != ComparatorAvailable && facts.Comparator != ComparatorMissing && facts.Comparator != ComparatorNotRequired {
			return Plan{}, fmt.Errorf("worktree comparison target observation is absent or invalid")
		}
		if facts.Comparator != ComparatorAvailable {
			return Plan{}, fmt.Errorf("cannot verify unpushed commits: no comparison target is available")
		}
		if facts.Containment != Contained && facts.Containment != NotContained && facts.Containment != ContainmentNotRequired {
			return Plan{}, fmt.Errorf("worktree containment observation is absent or invalid")
		}
		if facts.Containment != Contained {
			return Plan{}, fmt.Errorf("worktree HEAD is not contained in the comparison target")
		}
	} else if facts.Comparator != ComparatorNotRequired || facts.Containment != ContainmentNotRequired {
		return Plan{}, fmt.Errorf("worktree force-mode comparison observations are inconsistent")
	}
	plan := Plan{approved: true, mode: request.Mode, targetPath: facts.RegisteredPath}
	if facts.ManagedIgnore == IgnoreManaged {
		plan.managedIgnoreEntry = facts.ManagedIgnoreEntry
	}
	return plan, nil
}

// InvariantState is the adapter's raw observation of one independently
// revalidated invariant.
type InvariantState uint8

const (
	// InvariantUnknown means the adapter could not complete the observation.
	InvariantUnknown InvariantState = iota

View on GitHub (pinned to 71377f2769)

Solutions

  1. Push the worktree branch (git push) so the comparator contains its HEAD, then retry
  2. Merge/rebase the worktree commits into the target branch and push
  3. Use Force mode only if orphaning the commits is intentional

Example fix

// before
plan, err := worktreeremove.Prepare(req, facts) // Normal mode, Containment == NotContained
// after
// git push in the worktree, re-observe facts (Containment becomes Contained), then:
plan, err := worktreeremove.Prepare(req, facts)
Defensive patterns

Strategy: validation

Validate before calling

if req.Mode == worktreeremove.Normal && facts.Containment != worktreeremove.Contained {
	return fmt.Errorf("worktree HEAD not pushed; push or use Force")
}

Type guard

func headPushed(c worktreeremove.Containment) bool {
	return c == worktreeremove.Contained
}

Try / catch

plan, err := worktreeremove.Prepare(req, facts)
if err != nil && strings.Contains(err.Error(), "not contained in the comparison target") {
	return fmt.Errorf("push/merge worktree commits or use Force deliberately: %w", err)
}

Prevention

When it happens

Trigger: Calling Prepare with Mode: Normal while the worktree branch has commits not present in the upstream/comparison ref (Containment == NotContained).

Common situations: Work done locally that was never pushed or merged; a rebase that rewrote commits so the upstream no longer contains the old HEAD; a comparator pointing at a branch missing recent commits.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/bd15330713052f62. Report an issue: GitHub.