gastownhall/beads · error

cannot verify unpushed commits: no comparison target is avai

Error message

cannot verify unpushed commits: no comparison target is available

What it means

Prepare rejects the request because mode is Normal and facts.Comparator is ComparatorMissing or ComparatorNotRequired — no comparison target (e.g. upstream branch) exists to prove the worktree HEAD was pushed. Removing could orphan commits, so the library fails closed.

Source

Thrown at internal/worktreeremove/policy.go:208

	}
	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")
	}
	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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Push the worktree branch to a remote (git push -u origin <branch>) so a comparison target exists
  2. Push the worktree HEAD to a ref the comparator resolves, then retry
  3. Use Force mode only if losing the commits is acceptable

Example fix

// before
plan, err := worktreeremove.Prepare(req, facts) // Normal mode, Comparator == ComparatorMissing
// after
// git push -u origin <branch> in the worktree, re-observe facts, then:
plan, err := worktreeremove.Prepare(req, facts)
Defensive patterns

Strategy: validation

Validate before calling

if facts.Comparator != worktreeremove.ComparatorAvailable {
	return fmt.Errorf("no upstream ref for %s; push or use Force", facts.RegisteredPath)
}

Type guard

func hasComparisonTarget(c worktreeremove.ComparatorKind) bool {
	return c == worktreeremove.ComparatorAvailable
}

Try / catch

plan, err := worktreeremove.Prepare(req, facts)
if err != nil && strings.Contains(err.Error(), "no comparison target is available") {
	return fmt.Errorf("push the branch (git push -u origin <branch>) or use Force: %w", err)
}

Prevention

When it happens

Trigger: Calling Prepare with Mode: Normal while the worktree branch has no upstream / comparison ref (Comparator != ComparatorAvailable).

Common situations: A local-only branch that was never pushed; the remote branch was deleted; upstream not configured after cloning a single branch.

Related errors


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