gastownhall/beads · error
worktree removal approval is absent or invalid
Error message
worktree removal approval is absent or invalid
What it means
Revalidate requires a Plan that was actually approved by Prepare. It calls plan.valid(), and if the Plan is the zero value, was never produced by Prepare, or was constructed manually without the internal approval flag, Revalidate refuses it before inspecting any facts. This guards against executing removal based on an unapproved plan.
Source
Thrown at internal/worktreeremove/policy.go:284
facts.GitMarkerBytes, facts.CommonDirectory, facts.Head, facts.Cleanliness,
facts.StatusBytes, facts.DirtyFileFingerprint, facts.ManagedIgnore,
} {
if state != InvariantStable {
return false
}
}
if plan.mode == Normal {
return facts.Comparator == InvariantStable && facts.Containment == InvariantStable
}
return (facts.Comparator == InvariantStable || facts.Comparator == InvariantNotRequired) &&
(facts.Containment == InvariantStable || facts.Containment == InvariantNotRequired)
}
// Revalidate refuses every unknown or changed invariant observed after
// Prepare. A zero or otherwise unapproved plan is always refused.
func Revalidate(plan Plan, facts RevalidationFacts) error {
if !plan.valid() {
return fmt.Errorf("worktree removal approval is absent or invalid")
}
if !revalidationValid(plan, facts) {
return fmt.Errorf("worktree changed before removal")
}
return nil
}
// RevalidationResult records whether reinspection completed without an
// adapter-side diagnostic after a failed removal.
type RevalidationResult uint8
const (
// RevalidationResultUnknown means reinspection did not report a result.
RevalidationResultUnknown RevalidationResult = iota
// RevalidationPassed means reinspection completed without a diagnostic.
RevalidationPassed
// RevalidationFailed means reinspection produced an adapter diagnostic.
RevalidationFailedView on GitHub (pinned to 71377f2769)
Solutions
- Only pass a Plan obtained from a successful Prepare call — check Prepare's error first.
- Do not round-trip Plan through JSON/DB; keep it in memory for the same process that created it.
- In tests, use the mustPrepare/test helper used by the package's own tests instead of constructing Plan literals.
Example fix
// before
plan, _ := Prepare(req, facts)
err := Revalidate(plan, rfacts)
// after
plan, err := Prepare(req, facts)
if err != nil {
return err
}
err = Revalidate(plan, rfacts) Defensive patterns
Strategy: validation
Validate before calling
plan, err := Prepare(req, facts)
if err != nil {
return fmt.Errorf("prepare failed: %w", err)
}
// plan is guaranteed approved; safe for Revalidate/ClassifyFailure Prevention
- Always check Prepare's error before using the returned Plan.
- Never construct Plan literals; treat it as opaque output of Prepare.
- Do not serialize/deserialize Plan across process boundaries.
When it happens
Trigger: Calling Revalidate with a zero Plan{}; constructing Plan{mode: ..., targetPath: ...} directly without going through Prepare; passing a Plan from a Prepare call that returned an error (Prepare returns Plan{} on failure).
Common situations: Ignoring the error from Prepare and using the returned Plan anyway; serializing/deserializing a Plan so the unexported approval field is lost; writing unit tests that hand-build a Plan struct.
Related errors
- failed to inspect created worktree cleanliness: %w %s
- created worktree is dirty after checkout; refusing to contin
- worktree not found: %s
- failed to read git worktree registry: %w
- git worktree registry is empty
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/36228c91cadc91e1.
Report an issue: GitHub.