gastownhall/beads · warning · publicops.ErrValidation

root id must not be empty

Error message

root id must not be empty

What it means

ValidateWalkTreeRequest is the pure, shared validation behind WalkDependencyTreeInTx on all backends; it refuses a WalkTreeRequest whose RootID is empty, wrapping publicops.ErrValidation with 'root id must not be empty'. This is a programmer/caller input error, never a database condition — the check runs before any transaction is opened.

Source

Thrown at internal/storage/issueops/tree_walk.go:27

)

// The dependency-tree WALK: the shared body behind issueops.TreeWalker on all
// three backends, split into a pure half and a transactional half so the parts
// that decide what the answer MEANS are testable without a database.
//
// ValidateWalkTreeRequest holds the request vocabulary, PruneTreeByStatus the
// ancestor-keeping rule and MergeBidirectionalTree the two-walk concatenation;
// all three are pure and pinned in tree_walk_test.go. WalkDependencyTreeInTx
// needs a transaction because the root probe, the recursion and the hydration
// must see one database state, and for a `both` walk that covers BOTH
// directions.

// ValidateWalkTreeRequest checks a walk request against the vocabulary
// issueops.WalkTreeRequest documents and returns the normalized direction. It is
// pure and shared so that all three backends refuse in the same words.
func ValidateWalkTreeRequest(req publicops.WalkTreeRequest) (publicops.TreeDirection, error) {
	if req.RootID == "" {
		return "", fmt.Errorf("%w: root id must not be empty", publicops.ErrValidation)
	}
	direction := req.Direction
	if direction == "" {
		direction = publicops.TreeDown
	}
	switch direction {
	case publicops.TreeDown, publicops.TreeUp, publicops.TreeBoth:
	default:
		return "", fmt.Errorf("%w: direction %q must be one of %q, %q, %q",
			publicops.ErrValidation, req.Direction,
			publicops.TreeDown, publicops.TreeUp, publicops.TreeBoth)
	}
	if req.MaxDepth < 1 {
		return "", fmt.Errorf("%w: max depth must be at least 1, got %d", publicops.ErrValidation, req.MaxDepth)
	}
	if req.MaxRows < 0 {
		return "", fmt.Errorf("%w: max rows must not be negative, got %d", publicops.ErrValidation, req.MaxRows)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set req.RootID to a valid issue ID before calling the walk API.
  2. Use errors.Is(err, publicops.ErrValidation) to detect this and other validation refusals and surface a user-facing 'issue ID required' message.
  3. For CLI paths, validate the ID argument is non-empty before constructing the request.
  4. If the ID comes from a prior lookup, check that lookup's error first — an empty ID often masks an earlier failure.

Example fix

// before
req := publicops.WalkTreeRequest{Direction: publicops.TreeDown}
dir, err := issueops.ValidateWalkTreeRequest(req) // error: root id must not be empty
// after
if rootID == "" { return fmt.Errorf("issue ID required") }
req := publicops.WalkTreeRequest{RootID: rootID, Direction: publicops.TreeDown}
dir, err := issueops.ValidateWalkTreeRequest(req)
Defensive patterns

Strategy: validation

Validate before calling

func validWalkRequest(req issueops.WalkTreeRequest) error {
    if req.RootID == "" { return fmt.Errorf("root id required") }
    return nil
}

Type guard

func hasRootID(req issueops.WalkTreeRequest) bool {
    return strings.TrimSpace(req.RootID) != ""
}

Try / catch

dir, err := issueops.ValidateWalkTreeRequest(req)
if err != nil {
    if errors.Is(err, publicops.ErrValidation) {
        return fmt.Errorf("invalid tree walk request: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling WalkDependencyTreeInTx (or the TreeWalker API) with publicops.WalkTreeRequest{RootID: ""} — e.g. an unset variable, a struct built from an empty CLI argument, or an ID field that was never populated.

Common situations: Scripting `bd dep tree` where the ID argument is missing; passing an issue struct's ID straight through when the issue lookup failed earlier; deserializing a request from JSON that omitted root_id; copy-pasted code paths that forget to set RootID.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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