gastownhall/beads · error
db: DependencySQLRepository.Insert: DependsOnID must not be
Error message
db: DependencySQLRepository.Insert: DependsOnID must not be empty
What it means
DependencySQLRepository.Insert returns this error when dep.DependsOnID is empty. A dependency must name the issue it depends on; an empty DependsOnID is rejected before any SQL executes. It is part of Insert's defensive validation chain (nil → IssueID → DependsOnID → self-dependency).
Source
Thrown at internal/storage/domain/db/dependency.go:79
return "depends_on_wisp_id", nil
case errors.Is(err, sql.ErrNoRows):
return "depends_on_issue_id", nil
case dberrors.IsTableNotExist(err):
return "depends_on_issue_id", nil
default:
return "", fmt.Errorf("classify dep target %s: %w", dependsOnID, err)
}
}
func (r *dependencySQLRepositoryImpl) Insert(ctx context.Context, dep *types.Dependency, actor string, opts domain.DepInsertOpts) error {
if dep == nil {
return errors.New("db: DependencySQLRepository.Insert: dep must not be nil")
}
if dep.IssueID == "" {
return errors.New("db: DependencySQLRepository.Insert: IssueID must not be empty")
}
if dep.DependsOnID == "" {
return errors.New("db: DependencySQLRepository.Insert: DependsOnID must not be empty")
}
if dep.IssueID == dep.DependsOnID {
// Lead with the sentinel so this defensive repo-layer guard renders like
// every other self-dep site ("cannot add self-dependency: X cannot depend
// on itself") instead of appending the sentinel text.
return fmt.Errorf("db: DependencySQLRepository.Insert: %w: %s cannot depend on itself", domain.ErrSelfDependency, dep.IssueID)
}
metadata := dep.Metadata
if metadata == "" {
metadata = "{}"
}
if !opts.HierarchyValidated {
if err := r.ValidateBlockingHierarchy(ctx, dep); err != nil {
return err
}
}View on GitHub (pinned to 71377f2769)
Solutions
- Set dep.DependsOnID to the target issue's ID before calling Insert.
- Fix the CLI/input path so the target ID is always captured and validated early.
- Check for swapped IssueID/DependsOnID assignments.
Example fix
// before
dep := &types.Dependency{IssueID: parent.ID}
repo.Insert(ctx, dep, actor, opts)
// after
dep := &types.Dependency{IssueID: parent.ID, DependsOnID: target.ID}
repo.Insert(ctx, dep, actor, opts) Defensive patterns
Strategy: validation
Validate before calling
if dep == nil || dep.DependsOnID == "" {
return fmt.Errorf("DependsOnID required")
} Try / catch
if err := repo.Insert(ctx, dep, actor, opts); err != nil {
if strings.Contains(err.Error(), "DependsOnID must not be empty") {
// populate DependsOnID and retry
}
} Prevention
- Require the target issue argument in CLI input parsing
- Validate both IDs at the boundary before repo calls
- Use a constructor that requires IssueID and DependsOnID
When it happens
Trigger: Calling Insert with a *types.Dependency whose DependsOnID was never set — e.g. building the dependency from the owning issue's side only, or empty input from CLI/JSON where the target was omitted.
Common situations: CLI command invoked without the target issue argument; deserialization dropping the DependsOnID field; tests constructing minimal Dependency structs; fields swapped between IssueID and DependsOnID.
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
- db: DependencySQLRepository.Insert: dep must not be nil
- db: DependencySQLRepository.Insert: IssueID must not be empt
- db: DependencySQLRepository.ValidateBlockingHierarchy: dep m
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9194f6935255e572.
Report an issue: GitHub.