gastownhall/beads · error · storage.ErrValidation
%w: count edges requires a direction (%q or %q)
Error message
%w: count edges requires a direction (%q or %q)
What it means
ValidateEdgeCountRequest rejects an EdgeCountRequest whose Direction field is empty, wrapping storage.ErrValidation. An edge count is inherently directional (inbound or outbound), so the library refuses to guess. This sentinel-wrapped error lets callers distinguish caller mistakes from legitimately empty results.
Source
Thrown at internal/storage/issueops/edge_counts.go:39
var edgeCountPlanes = []struct{ dependencies, sources string }{
{dependencies: "dependencies", sources: "issues"},
{dependencies: "wisp_dependencies", sources: "wisps"},
}
// ValidateEdgeCountRequest applies the request rules every GraphCounter
// implementation shares.
//
// THE ORDER IS PART OF THE CONTRACT. The direction is checked FIRST, so an
// empty request is a refusal about the direction rather than an empty answer:
// EdgeCountRequest{} names no anchors, and answering it with no anchors would
// let a caller that forgot the direction get a plausible response forever. The
// per-entry checks that follow tell a caller's mistake from a legitimately
// empty answer, exactly as ValidateEdgeReadRequest's do.
func ValidateEdgeCountRequest(request publicops.EdgeCountRequest) error {
switch request.Direction {
case publicops.EdgeDirectionIn, publicops.EdgeDirectionOut:
case "":
return fmt.Errorf("%w: count edges requires a direction (%q or %q)",
storage.ErrValidation, publicops.EdgeDirectionOut, publicops.EdgeDirectionIn)
default:
return fmt.Errorf("%w: count edges direction %q is not %q or %q",
storage.ErrValidation, request.Direction, publicops.EdgeDirectionOut, publicops.EdgeDirectionIn)
}
if request.Status != "" && request.Direction != publicops.EdgeDirectionIn {
return fmt.Errorf("%w: count edges status %q needs direction %q: an outbound edge's far end may be a row this database does not hold",
storage.ErrValidation, request.Status, publicops.EdgeDirectionIn)
}
for i, id := range request.IDs {
if id == "" {
return fmt.Errorf("%w: count edges id %d is empty", storage.ErrValidation, i)
}
}
for i, depType := range request.Types {
if !depType.IsValid() {
return fmt.Errorf("%w: count edges type %d is not a usable dependency type (non-empty, max %d chars)",
storage.ErrValidation, i, types.MaxDependencyTypeLen)View on GitHub (pinned to 71377f2769)
Solutions
- Set request.Direction to publicops.EdgeDirectionIn or publicops.EdgeDirectionOut before calling
- Check errors.Is(err, storage.ErrValidation) to detect this as a caller-input bug rather than a data problem
- Add a default direction at the CLI/API boundary when the user doesn't specify one
- Validate the request before executing to produce a friendlier message
Example fix
// before
req := publicops.EdgeCountRequest{IssueID: "BD-1"} // Direction omitted
count, err := ExecuteEdgeCount(ctx, db, req)
// after
req := publicops.EdgeCountRequest{IssueID: "BD-1", Direction: publicops.EdgeDirectionOut}
count, err := ExecuteEdgeCount(ctx, db, req) Defensive patterns
Strategy: validation
Validate before calling
func validateEdgeCountRequest(req publicops.EdgeCountRequest) error {
if req.Direction == "" {
return fmt.Errorf("direction is required: %q or %q",
publicops.EdgeDirectionOut, publicops.EdgeDirectionIn)
}
return nil
} Try / catch
err := issueops.ExecuteEdgeCount(ctx, db, req)
if err != nil {
if errors.Is(err, storage.ErrValidation) {
return fmt.Errorf("bad request: %w", err) // 400-style caller error, not a data failure
}
return err
} Prevention
- Always initialize Direction from the EdgeDirection constants when building requests
- Add a default direction at the CLI/JSON boundary when the flag is absent
- Use errors.Is(err, storage.ErrValidation) to separate input bugs from empty results
- Cover request construction with a unit test asserting Direction is set
When it happens
Trigger: Calling ExecuteEdgeCount (or building an EdgeCountRequest) with EdgeCountRequest.Direction left as "" — e.g. a struct literal that omits Direction, or a CLI/JSON decode that left the field unset.
Common situations: JSON payloads missing the "direction" key; refactoring that added new request fields but didn't set Direction; tests constructing requests by field name omitting Direction.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- %w: count edges direction %q is not %q or %q
- node %q has empty title
- node %q: dep %d has empty target
- edge %d: must specify from_key or from_id
- edge %d %s->%s duplicates a parent-child relationship with d
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6146f37f1acbb6d8.
Report an issue: GitHub.