gastownhall/beads · warning · storage.ErrValidation
%w: add dependencies edge %d requires a dependency type (max
Error message
%w: add dependencies edge %d requires a dependency type (max %d chars)
What it means
Each dependency edge must carry a valid dependency type (edge.Type.IsValid()) — non-empty and no longer than types.MaxDependencyTypeLen. Beads deliberately treats the type vocabulary as open (workspace-configurable), so the check is only for presence/length, not membership in a fixed list. Failures are wrapped with storage.ErrValidation.
Source
Thrown at internal/storage/issueops/dependency_editor.go:40
// SkipPerEdgeCycleCheck would skip it entirely.
//
// The type check is that there IS a type — non-empty, within the column's
// length. It is deliberately not a membership test: the vocabulary is an open,
// workspace-configurable set (see the Dep* constants), so refusing an unlisted
// type would refuse a workspace's own.
func ValidateAddDependenciesRequest(request publicops.AddDependenciesRequest) error {
if request.Actor == "" {
return fmt.Errorf("%w: add dependencies requires an actor", storage.ErrValidation)
}
if len(request.Edges) == 0 {
return fmt.Errorf("%w: add dependencies requires at least one edge", storage.ErrValidation)
}
for i, edge := range request.Edges {
if edge.IssueID == "" || edge.DependsOnID == "" {
return fmt.Errorf("%w: add dependencies edge %d requires both endpoints", storage.ErrValidation, i)
}
if !edge.Type.IsValid() {
return fmt.Errorf("%w: add dependencies edge %d requires a dependency type (max %d chars)",
storage.ErrValidation, i, types.MaxDependencyTypeLen)
}
if edge.IssueID == edge.DependsOnID {
return fmt.Errorf("%w: %s cannot depend on itself", domain.ErrSelfDependency, edge.IssueID)
}
}
return nil
}
// ValidateRemoveDependencyRequest applies the request rules every
// DependencyEditor implementation shares for a removal.
func ValidateRemoveDependencyRequest(request publicops.RemoveDependencyRequest) error {
if request.Actor == "" {
return fmt.Errorf("%w: remove dependency requires an actor", storage.ErrValidation)
}
if request.IssueID == "" || request.DependsOnID == "" {
return fmt.Errorf("%w: remove dependency requires both endpoints", storage.ErrValidation)
}View on GitHub (pinned to 71377f2769)
Solutions
- Set a known type constant (e.g. types.DepBlocks) or a valid workspace-configured type on every edge.
- Truncate or reject custom type names longer than types.MaxDependencyTypeLen before calling.
- Default empty types to the canonical value your workflow expects.
- Match errors.Is(err, storage.ErrValidation) to confirm it's this check.
Example fix
// before
edge := publicops.DependencyEdge{IssueID: a, DependsOnID: b} // Type empty
// after
edge := publicops.DependencyEdge{IssueID: a, DependsOnID: b, Type: types.DepBlocks} Defensive patterns
Strategy: validation
Validate before calling
for i, e := range req.Edges {
if !e.Type.IsValid() {
return fmt.Errorf("edge %d has invalid type %q", i, e.Type)
}
} Type guard
func validEdgeType(t types.DependencyType) bool {
return t != "" && len(t) <= types.MaxDependencyTypeLen
} Try / catch
err := store.AddDependencies(ctx, req)
if errors.Is(err, storage.ErrValidation) {
// fix the type (set a known constant or truncate) and retry
} Prevention
- Default edge types to a known constant instead of leaving zero value.
- Clamp custom type names to types.MaxDependencyTypeLen at input time.
- Use types.Dep* constants rather than raw strings.
When it happens
Trigger: Calling AddDependencies with an edge whose Type is the zero value ("") or exceeds MaxDependencyTypeLen characters — e.g. free-form type strings from user input that weren't truncated.
Common situations: Import scripts passing arbitrary dependency labels without validating length; forgetting to default the type when constructing edges manually; schema changes that introduced longer custom types.
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
- no store is open for this workspace
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
- ExternalDoltConfig: must set Socket or (Host, Port)
- ExternalDoltConfig: Host requires Port
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a30323b45f050381.
Report an issue: GitHub.