gastownhall/beads · error
db: DependencySQLRepository.Insert: IssueID must not be empt
Error message
db: DependencySQLRepository.Insert: IssueID must not be empty
What it means
DependencySQLRepository.Insert returns this error when dep.IssueID is empty. The dependency must record which issue owns the dependency, so an empty IssueID is rejected before any SQL executes. It is a defensive repo-layer guard in the same validation chain as the nil and DependsOnID checks.
Source
Thrown at internal/storage/domain/db/dependency.go:76
err := r.runner.QueryRowContext(ctx, "SELECT 1 FROM wisps WHERE id = ? LIMIT 1", dependsOnID).Scan(&probe)
switch {
case err == nil:
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 {View on GitHub (pinned to 71377f2769)
Solutions
- Set dep.IssueID to the owning issue's ID before calling Insert.
- Verify field order — if IssueID and DependsOnID were accidentally swapped, correct the assignment.
- Add upstream validation (service/command layer) that both IDs are present.
Example fix
// before
dep := &types.Dependency{DependsOnID: target.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.IssueID == "" {
return fmt.Errorf("IssueID required")
} Try / catch
if err := repo.Insert(ctx, dep, actor, opts); err != nil {
if strings.Contains(err.Error(), "IssueID must not be empty") {
// populate IssueID and retry
}
} Prevention
- Validate both IDs in the CLI/service layer before repo calls
- Use a constructor that requires IssueID and DependsOnID
- Watch for swapped IssueID/DependsOnID assignments
When it happens
Trigger: Calling Insert with a *types.Dependency whose IssueID field was never set — e.g. constructing the dependency from the 'depends-on' side only, or a partially filled struct from deserialization.
Common situations: Building dependencies from JSON/CLI input where the owning issue ID was omitted; swapping IssueID/DependsOnID by mistake; tests constructing minimal Dependency structs.
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: DependsOnID must not be
- 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/6be24088ccc32ed3.
Report an issue: GitHub.