gastownhall/beads · error

registered worktree target is absent or invalid

Error message

registered worktree target is absent or invalid

What it means

Prepare rejects a RegisteredTarget whose supporting facts are incomplete: either facts.Target is not RegisteredTarget in the switch, or one of the required Present-facts (Registration, RegisteredPath, TargetDir, GitAdminDir, GitMarker, Head) is missing/empty. Prepare refuses to plan a removal against an under-verified target — every invariant must be observed before approval.

Source

Thrown at internal/worktreeremove/policy.go:181

}

func (plan Plan) valid() bool {
	return plan.approved && (plan.mode == Normal || plan.mode == Force) && plan.targetPath != ""
}

// Prepare validates the initial observations and returns an approved plan.
func Prepare(request Request, facts PrepareFacts) (Plan, error) {
	if request.Mode != Normal && request.Mode != Force {
		return Plan{}, fmt.Errorf("worktree removal mode is absent or invalid")
	}
	switch facts.Target {
	case PrimaryWorktree:
		return Plan{}, fmt.Errorf("cannot remove the primary worktree")
	case CurrentWorktree:
		return Plan{}, fmt.Errorf("cannot remove the worktree containing the running command")
	case RegisteredTarget:
	default:
		return Plan{}, fmt.Errorf("registered worktree target is absent or invalid")
	}
	if facts.Registration != Present || facts.RegisteredPath == "" ||
		facts.TargetDir != Present || facts.GitAdminDir != Present ||
		facts.GitMarker != Present || facts.Head != Present {
		return Plan{}, fmt.Errorf("registered worktree target is absent or invalid")
	}
	if facts.CommonDir != Matched {
		return Plan{}, fmt.Errorf("target common git directory does not match repository")
	}
	if facts.Status != Clean && facts.Status != Dirty {
		return Plan{}, fmt.Errorf("worktree cleanliness observation is absent or invalid")
	}
	if facts.ManagedIgnore != IgnoreAbsent && facts.ManagedIgnore != IgnoreManaged {
		return Plan{}, fmt.Errorf("managed ignore observation is absent or invalid")
	}
	if (facts.ManagedIgnore == IgnoreManaged) != (facts.ManagedIgnoreEntry != "") {
		return Plan{}, fmt.Errorf("managed ignore cleanup identity is absent or invalid")
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Populate PrepareFacts by running the full observation set (registration lookup, target dir stat, git admin dir, git marker, HEAD) immediately before Prepare.
  2. Re-gather facts if the worktree changed on disk; do not reuse cached facts from before a mutation.
  3. Verify the user-supplied path matches the registered worktree path exactly (filepath.Clean/abs both sides).
  4. If the worktree is already partially deleted, use the force/cleanup path appropriate for that state instead of a Normal removal.

Example fix

// before
plan, err := worktreeremove.Prepare(req, worktreeremove.PrepareFacts{
    Target:        worktreeremove.RegisteredTarget,
    RegisteredPath: path,
    // Registration, TargetDir, GitAdminDir, GitMarker, Head left zero
})

// after
facts := observeFacts(repo, path) // sets Registration, RegisteredPath, TargetDir,
                                  // GitAdminDir, GitMarker, Head
facts.Target = worktreeremove.RegisteredTarget
plan, err := worktreeremove.Prepare(req, facts)
Defensive patterns

Strategy: validation

Validate before calling

func factsComplete(f worktreeremove.PrepareFacts) bool {
    return f.Target == worktreeremove.RegisteredTarget &&
        f.Registration == worktreeremove.Present && f.RegisteredPath != "" &&
        f.TargetDir == worktreeremove.Present && f.GitAdminDir == worktreeremove.Present &&
        f.GitMarker == worktreeremove.Present && f.Head == worktreeremove.Present
}

Try / catch

plan, err := worktreeremove.Prepare(req, facts)
if err != nil && strings.Contains(err.Error(), "registered worktree target is absent or invalid") {
    return fmt.Errorf("target is not a fully registered worktree; re-run observation or use cleanup mode")
}

Prevention

When it happens

Trigger: Calling Prepare with Target: RegisteredTarget but facts such as Registration != Present, RegisteredPath == "", TargetDir/GitAdminDir/GitMarker/Head != Present; constructing PrepareFacts by hand without running the observation steps; a probe command failing upstream and its fact left zero-valued.

Common situations: Worktree directory already deleted by hand so TargetDir probes came back Absent; path passed by the user doesn't match the registered path; running the fact-gathering outside the repo so git admin dir/marker checks fail; stale facts cached from an earlier state.

Related errors


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