gastownhall/beads · error · publicops.ErrValidation

max depth must be at least 1, got %d

Error message

max depth must be at least 1, got %d

What it means

ValidateWalkTreeRequest requires MaxDepth to be at least 1; zero or negative values are rejected with this ErrValidation-wrapped error. The library throws it because a depth of 0 would visit no nodes, which cannot be a meaningful traversal, so the caller must explicitly request at least one level.

Source

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

// 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)
	}
	return direction, nil
}

// PruneTreeByStatus keeps every node carrying status, plus the ancestor chain of
// each survivor, in the walk order the nodes arrived in.
//
// KEEPING THE ANCESTORS IS WHAT MAKES THE ANSWER STILL A TREE. A bare filter
// would return nodes whose ParentID names something absent from the answer, and
// every renderer that rebuilds the shape from Depth and ParentID would then draw
// orphans at the wrong indentation.
//
// A PRUNE THAT MATCHES NOTHING RETURNS NOTHING, root included. The root survives
// only as somebody's ancestor, never for its own sake, so a tree with no
// matching member comes back empty rather than as a lone root — see

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set req.MaxDepth to a positive integer reflecting how deep you want to walk.
  2. If the field was left at its zero value, initialize it explicitly (e.g. a sensible default like 10).
  3. If you intended unlimited depth, pass a large finite value the library accepts, since 0 is invalid here.
  4. Fix flag/config parsing so missing depth maps to a positive default, not 0.

Example fix

// before
req := publicops.WalkDependencyTreeRequest{IssueID: id} // MaxDepth = 0
// after
req := publicops.WalkDependencyTreeRequest{IssueID: id, MaxDepth: 10}
Defensive patterns

Strategy: validation

Validate before calling

if req.MaxDepth < 1 {
	return fmt.Errorf("MaxDepth must be >= 1, got %d", req.MaxDepth)
}

Try / catch

if err := walk(ctx, req); errors.Is(err, publicops.ErrValidation) {
	// correct MaxDepth before retrying
}

Prevention

When it happens

Trigger: Calling WalkDependencyTreeInTx with req.MaxDepth == 0 (e.g. an uninitialized struct) or negative (e.g. -1 from a misparsed flag or a "no limit" sentinel).

Common situations: Leaving MaxDepth unset on a zero-value struct; using 0 to mean "unlimited" from another library's convention; parsing an optional --depth flag whose default is 0.

Related errors


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