gastownhall/beads · error

add dep: insert: %w

Error message

add dep: insert: %w

What it means

Wraps a repository Insert failure when persisting a new dependency edge after hierarchy and cycle validation already passed. Sentinels the caller is expected to see directly (DependencyTypeConflictError, DependencyHierarchyConflictError, DependencyEndpointNotFoundError) are passed through unwrapped; anything else — SQL errors, connection failures, constraint violations — is wrapped with this prefix so the failure point is identifiable.

Source

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

	if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp, HierarchyValidated: true, CycleValidated: true, EmitEvent: true}); err != nil {
		// The retype conflict is a user-facing error whose message already
		// matches embedded verbatim; pass it through unwrapped so the CLI does
		// not prepend "add dep: insert:" (#4547 F-1). The endpoint-existence
		// refusals are here for the same reason.
		var conflict *DependencyTypeConflictError
		if errors.As(err, &conflict) {
			return err
		}
		var hierarchyConflict *DependencyHierarchyConflictError
		if errors.As(err, &hierarchyConflict) {
			return err
		}
		var missingEndpoint *DependencyEndpointNotFoundError
		if errors.As(err, &missingEndpoint) {
			return err
		}
		return fmt.Errorf("add dep: insert: %w", err)
	}
	return nil
}

func (u *dependencyUseCaseImpl) RemoveDependency(ctx context.Context, issueID, dependsOnID, actor string) error {
	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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped cause to identify the storage-level failure (constraint, connection, transaction).
  2. Check whether the edge already exists (duplicate insert) before re-adding; delete or update instead.
  3. Verify the DB is writable and both endpoint issues exist; retry once the backend is healthy.
  4. For concurrency, serialize add operations per issue or retry idempotently — the insert is guarded by validation flags so a retry is safe.

Example fix

// before: treating insert failure as a validation problem
if err := uc.AddDependency(ctx, dep, actor); err != nil {
    return fmt.Errorf("invalid dependency: %w", err)
}
// after: distinguish passed-through sentinels from storage failures
if err := uc.AddDependency(ctx, dep, actor); err != nil {
    var conflict *domain.DependencyTypeConflictError
    if errors.As(err, &conflict) {
        return err // user-facing validation message
    }
    return fmt.Errorf("storage insert failed: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

var exists bool
if err := db.QueryRowContext(ctx,
    `SELECT COUNT(*) FROM dependencies WHERE issue_id=? AND depends_on_id=?`,
    dep.IssueID, dep.DependsOnID).Scan(&exists == nil, &exists); err == nil && exists {
    return nil // edge already present; skip insert
}

Type guard

func isDepConflict(err error) bool {
    var conflict *domain.DependencyTypeConflictError
    var hier *domain.DependencyHierarchyConflictError
    var missing *domain.DependencyEndpointNotFoundError
    return errors.As(err, &conflict) || errors.As(err, &hier) || errors.As(err, &missing)
}

Try / catch

if err := uc.AddDependency(ctx, dep, actor); err != nil {
    if isDepConflict(err) {
        return err // user-facing validation, handle distinctly
    }
    // otherwise it is "add dep: insert: ..." — a storage failure
    return fmt.Errorf("storage failure adding dep: %w", err)
}

Prevention

When it happens

Trigger: AddDependency or AddWispDependency reaches depRepo.Insert after validation succeeds, but the insert fails: DB connection lost between validate and write, unique/PK constraint hit on the dependencies row, Dolt transaction conflict, or context cancellation during the write.

Common situations: Two processes adding the same edge concurrently; database went read-only or disk full; interrupted Dolt transaction during a sync; stale connection pool after the DB server restarted mid-command.

Related errors


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