gastownhall/beads · error · storage.ErrValidation

%w: count edges type %d is not a usable dependency type (non

Error message

%w: count edges type %d is not a usable dependency type (non-empty, max %d chars)

What it means

ValidateEdgeCountRequest rejects an EdgeCountRequest whose Types slice contains a dependency type that fails types.DependencyType.IsValid() — empty or longer than types.MaxDependencyTypeLen. An unusable type would act as a filter that silently matches nothing, so it is refused up front. The error wraps storage.ErrValidation.

Source

Thrown at internal/storage/issueops/edge_counts.go:56

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

// FinishEdgeCount assembles the per-anchor answer from the two things every
// implementation reads: which anchors exist, and the edge tallies keyed by
// anchor.
//
// It is a pure function beside the body for the reason the checklist gives: the
// parts that decide what the answer MEANS are pinned in milliseconds without a
// database, and the conformance contract is left to assert what only a real
// backend can show. What it decides here is the whole of the missing-anchor
// rule — a missing anchor counts 0 whatever rows are keyed to it, and a present
// anchor with no matching edges counts 0 too, which are the two facts a caller
// tells apart by Missing and by nothing else.
func FinishEdgeCount(anchors []string, present map[string]struct{}, tallies map[string]int64) publicops.EdgeCountResult {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Validate each type with types.DependencyType.IsValid() before building the request and drop/repair bad entries.
  2. Normalize input: strings.TrimSpace and enforce the MaxDependencyTypeLen limit at the boundary.
  3. Use the predefined dependency-type constants (e.g. types.DepBlocks, types.DepRelated) instead of raw strings.
  4. If a version change renamed types, update the callers/config to the current names.

Example fix

// before
req := publicops.EdgeCountRequest{IDs: ids, Direction: "in", Types: []types.DependencyType{userType}}
// after
userType = types.DependencyType(strings.TrimSpace(string(userType)))
if userType != "" && userType.IsValid() {
	req := publicops.EdgeCountRequest{IDs: ids, Direction: "in", Types: []types.DependencyType{userType}}
}
Defensive patterns

Strategy: validation

Validate before calling

func validTypes(ts []types.DependencyType) bool {
	for _, t := range ts { if !t.IsValid() { return false } }
	return true
}

Type guard

func filterValidTypes(ts []types.DependencyType) []types.DependencyType {
	return slices.DeleteFunc(slices.Clone(ts), func(t types.DependencyType) bool { return !t.IsValid() })
}

Try / catch

if err := ValidateEdgeCountRequest(req); err != nil {
	if errors.Is(err, storage.ErrValidation) && strings.Contains(err.Error(), "dependency type") {
		// sanitize types and rebuild the request
	}
}

Prevention

When it happens

Trigger: CountEdges / ExecuteEdgeCount called with Types: []types.DependencyType{""} or a type string exceeding MaxDependencyTypeLen; constructing a type via types.DependencyType(rawUserInput) without validation.

Common situations: Free-form user input passed through as a dependency type; a typo like "relates-to " (trailing space inflating length); config or flags supplying a type renamed in a newer beads version; truncation or copying adding whitespace.

Related errors


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