gastownhall/beads · error · storage.ErrValidation
%w: remove dependency requires an actor
Error message
%w: remove dependency requires an actor
What it means
ValidateRemoveDependencyRequest rejects a RemoveDependencyRequest whose Actor field is empty, wrapping storage.ErrValidation. Every dependency mutation is attributed to an actor so the change is recorded durably in history; a removal without an actor cannot be audited, so the request is refused before any database work. The %w wrap lets callers match errors.Is(err, storage.ErrValidation).
Source
Thrown at internal/storage/issueops/dependency_editor.go:54
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)
}
return nil
}
// AddDependenciesCommitMessage is the history entry an edge assertion records.
//
// Unlike CloseBatchCommitMessage it is composed from the REQUEST rather than
// the result, and that difference is the contract's: the request is
// all-or-nothing, so what was asked for is exactly what landed or nothing did.
// A batch close has to name what landed because it can skip an id; this cannot.
//
// The two spellings are the two the CLI already wrote — one edge names both
// endpoints, several name their count — because the role is what both `bd dep
// add <a> <b>` and `bd dep add --file` now go through, and `bd dolt log` should
// keep reading the way it did. The count spelling is keyed on the EDGE COUNTView on GitHub (pinned to 71377f2769)
Solutions
- Set request.Actor to the acting user or agent identity (e.g. the 'bd' configured user or requesting principal) before invoking the remove-dependency operation.
- If the caller genuinely has no identity, use the deployment's default actor (e.g. the value bd uses for local unattended changes) rather than an empty string.
- Check upstream translation code (CLI flags, HTTP handlers) to ensure the actor is propagated into the request struct instead of being dropped.
Example fix
// before
req := publicops.RemoveDependencyRequest{IssueID: "bd-42", DependsOnID: "bd-7"}
err := editor.ExecuteRemoveDependency(ctx, req)
// after
req := publicops.RemoveDependencyRequest{IssueID: "bd-42", DependsOnID: "bd-7", Actor: "alice"}
err := editor.ExecuteRemoveDependency(ctx, req) Defensive patterns
Strategy: validation
Validate before calling
if req.Actor == "" {
return fmt.Errorf("remove dependency: actor is required (set the acting user/agent)")
}
// proceed with req Prevention
- Always construct requests through a helper that requires an actor parameter, never a bare struct literal.
- In CLI/API layers, default the actor from config (e.g. bd's configured user) instead of leaving it empty.
- Add a unit test asserting the actor-propagation path for every remove-dependency entry point.
When it happens
Trigger: Calling any DependencyEditor implementation's remove-dependency path (which runs ValidateRemoveDependencyRequest) with a publicops.RemoveDependencyRequest where request.Actor == "" — e.g. a struct literal that only sets IssueID and DependsOnID, or a caller that drops the actor value when translating from a CLI/API layer.
Common situations: Hand-constructing RemoveDependencyRequest in tests or scripts and forgetting the Actor field; a CLI command that has no --actor flag configured so the value defaults to empty; refactors that renamed or removed an actor parameter upstream leaving the zero value.
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
- 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/fc66cc594c456096.
Report an issue: GitHub.