gastownhall/beads · error · publicops.ErrValidation
direction %q must be one of %q, %q, %q
Error message
direction %q must be one of %q, %q, %q
What it means
ValidateWalkTreeRequest rejects a WalkDependencyTreeRequest whose Direction field is not one of the three supported traversal directions (down, up, both). The library throws this wrapped in publicops.ErrValidation because the caller supplied an unrecognized direction string, likely a typo or a value copied from a different API. An empty direction is silently defaulted to TreeDown, so only non-empty invalid values reach this error.
Source
Thrown at internal/storage/issueops/tree_walk.go:36
// 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)
}
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 drawView on GitHub (pinned to 71377f2769)
Solutions
- Set req.Direction to one of publicops.TreeDown, publicops.TreeUp, or publicops.TreeBoth.
- Leave req.Direction empty ("") to accept the default (TreeDown).
- Map/normalize user input against the three constants before constructing the request.
- Check that you are not importing similarly-named constants from a different package or an outdated version.
Example fix
// before
req := publicops.WalkDependencyTreeRequest{IssueID: id, Direction: "down"}
// after
req := publicops.WalkDependencyTreeRequest{IssueID: id, Direction: publicops.TreeDown} Defensive patterns
Strategy: validation
Validate before calling
func validDirection(d string) bool {
return d == "" || d == publicops.TreeDown || d == publicops.TreeUp || d == publicops.TreeBoth
}
if !validDirection(req.Direction) {
return fmt.Errorf("direction %q not supported", req.Direction)
} Try / catch
if err := walk(ctx, req); errors.Is(err, publicops.ErrValidation) {
// fix request fields; do not retry
} Prevention
- Always use the exported TreeDown/TreeUp/TreeBoth constants rather than string literals.
- Validate CLI/JSON input against the constant set before building the request.
- Rely on the empty-string default instead of inventing your own "default" value.
When it happens
Trigger: Calling WalkDependencyTreeInTx (directly or via WalkDependencyTree) with req.Direction set to any string other than publicops.TreeDown, TreeUp, or TreeBoth, e.g. "downward", "children", "D", or a stale constant from an older SDK version.
Common situations: Hand-building the request struct instead of using helpers; renaming between API versions where direction constants changed; user-supplied CLI input passed straight into Direction without mapping.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- max depth must be at least 1, got %d
- max rows must not be negative, got %d
- no store is open for this workspace
- not found
- no absolute native user directory is available
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dfb690dae51f4a88.
Report an issue: GitHub.