gastownhall/beads · error
remove dep: sourceID and dependsOnID must not be empty
Error message
remove dep: sourceID and dependsOnID must not be empty
What it means
A guard error from RemoveDependencyBySource: one or both of the edge endpoint IDs (sourceID, dependsOnID) is the empty string. The use case cannot address, classify, or delete an edge without both endpoints, so it refuses before touching the repository. It is a caller-input validation failure, not a storage problem.
Source
Thrown at internal/storage/domain/dependency.go:348
return u.removeDep(ctx, issueID, dependsOnID, actor, false)
}
func (u *dependencyUseCaseImpl) RemoveWispDependency(ctx context.Context, wispID, dependsOnID, actor string) error {
return u.removeDep(ctx, wispID, dependsOnID, actor, true)
}
// RemoveDependencyBySource removes one edge from the plane its SOURCE lives in
// and reports whether there was an edge to remove.
//
// It is the source-routed twin of AddDependencies, and exists for the same
// reason: `bd dep remove` takes whatever id the caller names, and pinning the
// removal to the durable table means failing to remove an edge whose source is
// a wisp while reporting that it was never there (bd-yby99.17). The delete IS
// the verdict, the way the store-backed body reads it off RemoveDependencyInTx
// rather than from a separate lookup.
func (u *dependencyUseCaseImpl) RemoveDependencyBySource(ctx context.Context, sourceID, dependsOnID, actor string) (bool, error) {
if sourceID == "" || dependsOnID == "" {
return false, fmt.Errorf("remove dep: sourceID and dependsOnID must not be empty")
}
wispSources, err := u.depRepo.WispSourceIDs(ctx, []string{sourceID})
if err != nil {
return false, fmt.Errorf("remove dep: classify source: %w", err)
}
_, sourceIsWisp := wispSources[sourceID]
res, err := u.depRepo.Delete(ctx, sourceID, dependsOnID, actor, DepInsertOpts{UseWispsTable: sourceIsWisp, EmitEvent: true})
if err != nil {
return false, fmt.Errorf("remove dep %s -> %s: %w", sourceID, dependsOnID, err)
}
return res.Found, nil
}
func (u *dependencyUseCaseImpl) removeDep(ctx context.Context, sourceID, dependsOnID, actor string, useWisp bool) error {
if sourceID == "" || dependsOnID == "" {
return fmt.Errorf("remove dep: sourceID and dependsOnID must not be empty")
}
if _, err := u.depRepo.Delete(ctx, sourceID, dependsOnID, actor, DepInsertOpts{UseWispsTable: useWisp, EmitEvent: true}); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Check both IDs for emptiness at the call site before invoking RemoveDependencyBySource.
- Trace where the empty ID comes from — fix the upstream lookup/parse so a real issue ID is produced.
- If the edge may not exist, look up the issue first and skip the removal call rather than passing a blank ID.
- Return a clear validation error to your user naming which ID was missing.
Example fix
// before
removed, err := uc.RemoveDependencyBySource(ctx, issueID, depID, actor)
// after
if issueID == "" || depID == "" {
return fmt.Errorf("cannot remove dep: issueID=%q depID=%q", issueID, depID)
}
removed, err := uc.RemoveDependencyBySource(ctx, issueID, depID, actor) Defensive patterns
Strategy: validation
Validate before calling
func validateEdgeIDs(sourceID, dependsOnID string) error {
if sourceID == "" {
return fmt.Errorf("sourceID is empty")
}
if dependsOnID == "" {
return fmt.Errorf("dependsOnID is empty")
}
return nil
}
if err := validateEdgeIDs(sourceID, dependsOnID); err != nil {
return err
}
removed, err := uc.RemoveDependencyBySource(ctx, sourceID, dependsOnID, actor) Type guard
func hasEdgeIDs(sourceID, dependsOnID string) bool {
return sourceID != "" && dependsOnID != ""
} Try / catch
if err := validateEdgeIDs(sourceID, dependsOnID); err != nil {
return fmt.Errorf("cannot remove dep: %w", err)
}
_, err := uc.RemoveDependencyBySource(ctx, sourceID, dependsOnID, actor) Prevention
- Never pass CLI/script variables without checking they resolved to a real ID
- Validate struct fields are populated before calling removal APIs
- In batch loops, skip entries with empty endpoint IDs and log them
- Look up the issue first; if not found, skip removal instead of passing blank IDs
When it happens
Trigger: Calling RemoveDependencyBySource(ctx, "", dependsOnID, actor) or with an empty dependsOnID — typically because an upstream lookup returned no ID, a parsed CLI argument was blank, or a struct field was never populated before the call.
Common situations: Scripting `bd dep remove` with a variable that failed to resolve; parsing issue IDs from output where a column was empty; constructing the call from JSON where a key was missing and Go zero-values the string.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- reparent: childID must not be empty
- DeleteIssue: id must not be empty
- DeleteWisp: id must not be empty
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/102241ceee498da5.
Report an issue: GitHub.