gastownhall/beads · error

add deps[%d]: insert: %w

Error message

add deps[%d]: insert: %w

What it means

When inserting an individual dependency edge fails, AddDependencies wraps it with 'insert: %w'. Typed failures — *DependencyHierarchyConflictError and *DependencyEndpointNotFoundError — are returned unwrapped so callers can errors.As them; everything else (e.g. constraint violations, storage errors) gets this wrapper. It means the edge write itself was rejected or failed.

Source

Thrown at internal/storage/domain/dependency.go:762

			// create-with-deps, which calls depRepo.Insert directly without
			// EmitEvent. UseWispsTable routes both the edge and that event to
			// the source's own pair of tables.
			_, sourceIsWisp := wispSources[dep.IssueID]
			if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{
				UseWispsTable:      sourceIsWisp,
				HierarchyValidated: true,
				CycleValidated:     true,
				EmitEvent:          true,
			}); err != nil {
				var hierarchyConflict *DependencyHierarchyConflictError
				if errors.As(err, &hierarchyConflict) {
					return BulkAddDepsResult{}, err
				}
				var missingEndpoint *DependencyEndpointNotFoundError
				if errors.As(err, &missingEndpoint) {
					return BulkAddDepsResult{}, err
				}
				return BulkAddDepsResult{}, fmt.Errorf("add deps[%d]: insert: %w", i, err)
			}
		}
	}
	var pairs [][2]string
	for _, dep := range deps {
		if !types.IsSchedulingEdge(dep.Type) {
			continue
		}
		pairs = append(pairs, [2]string{dep.IssueID, dep.DependsOnID})
	}
	if len(pairs) > 0 {
		cyclePath, err := u.depRepo.CycleThroughEdges(ctx, pairs)
		if err != nil {
			return BulkAddDepsResult{}, fmt.Errorf("add deps: final cycle check: %w", err)
		}
		if cyclePath != "" {
			return BulkAddDepsResult{}, cycleErrorf("add deps: dependency cycle would be created: %s", cyclePath)
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped error after 'insert:' for the driver-level cause
  2. Re-check that both endpoint issues still exist before retrying
  3. If duplicates are expected, filter existing edges before the batch or tolerate the error per-edge
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure both endpoints exist before the call
for _, d := range deps {
    if _, err := issueRepo.Get(ctx, d.IssueID); err != nil { return err }
    if _, err := issueRepo.Get(ctx, d.DependsOnID); err != nil { return err }
}

Type guard

var miss *domain.DependencyEndpointNotFoundError
if errors.As(err, &miss) { /* handle missing endpoint typed path */ }

Try / catch

if err != nil {
    var hc *domain.DependencyHierarchyConflictError
    var me *domain.DependencyEndpointNotFoundError
    if errors.As(err, &hc) || errors.As(err, &me) { return err } // typed, unwrapped
    // 'insert:' wrapper — driver/constraint issue; inspect wrapped cause
}

Prevention

When it happens

Trigger: Calling AddDependencies where the per-edge repository insert returns a non-typed error — duplicate-edge constraint violations, storage write failures, or other driver errors not classified as hierarchy-conflict or missing-endpoint.

Common situations: Concurrent writers inserting the same edge; database read-only or disk full; referential issues when one endpoint was deleted between validation and insert.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/5b7996d33bbd8dd4. Report an issue: GitHub.