gastownhall/beads · error

delete: drop wisp deps: %w

Error message

delete: drop wisp deps: %w

What it means

Same cascade step as the regular 'drop deps' error but for the wisp-plane: depRepo.DeleteAllForIDs was called with UseWispsTable:true to remove dependency edges recorded in the wisps dependency table. A wrapped storage error here means wisp dependency rows could not be deleted and the whole deleteMany aborts.

Source

Thrown at internal/storage/domain/issue_delete.go:170

		for _, id := range allIDs {
			deletedSet[id] = true
		}
		connected, connectedIsWisp, err = u.collectConnectedIssues(ctx, allIDs, deletedSet)
		if err != nil {
			return result, err
		}
	}

	affectedIssues, affectedWisps, err := u.issueRepo.AffectedByDeletion(ctx, regularIDs, wispIDs)
	if err != nil {
		return result, fmt.Errorf("delete: affected by deletion: %w", err)
	}

	if _, err := u.depRepo.DeleteAllForIDs(ctx, regularIDs, DepInsertOpts{}); err != nil {
		return result, fmt.Errorf("delete: drop deps: %w", err)
	}
	if _, err := u.depRepo.DeleteAllForIDs(ctx, wispIDs, DepInsertOpts{UseWispsTable: true}); err != nil {
		return result, fmt.Errorf("delete: drop wisp deps: %w", err)
	}
	// The SYNC-PLANE edges pointing at a deleted wisp, which are not the same
	// rows as the line above and are not reached by a foreign key: there is no
	// FK from dependencies to wisps, so `dependencies.depends_on_wisp_id` rows
	// survive their target unless they are deleted explicitly. Without this a
	// forced delete of a wisp left its durable dependent holding an edge into
	// a row that no longer exists — dangling, not orphaned, which is not what
	// issueops.DeleteRequest.Force promises. The store body has always done
	// this (issueops.deleteIssueRowInTx -> DeleteWispFromDependenciesInTx).
	if _, err := u.depRepo.DeleteAllForIDs(ctx, wispIDs, DepInsertOpts{}); err != nil {
		return result, fmt.Errorf("delete: drop sync-plane edges into deleted wisps: %w", err)
	}
	if _, err := u.labelRepo.DeleteAllForIDs(ctx, regularIDs, LabelOpts{}); err != nil {
		return result, fmt.Errorf("delete: drop labels: %w", err)
	}
	if _, err := u.labelRepo.DeleteAllForIDs(ctx, wispIDs, LabelOpts{UseWispsTable: true}); err != nil {
		return result, fmt.Errorf("delete: drop wisp labels: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the wisps dependency table exists for your schema version; run migrations/doctor.
  2. Retry the delete; the transactional scope prevents partial state.
  3. Read the wrapped driver error for the root cause (lock timeout, permissions) and address it.
  4. Reduce batch size so the delete stays within connection/lock timeouts.
Defensive patterns

Strategy: validation

Validate before calling

if err := ctx.Err(); err != nil { return err }
exists, err := tableExists(ctx, "wisps_dependencies"); if err != nil || !exists { return fmt.Errorf("wisp dependency table missing — run migrations") }

Type guard

var dbe *storage.DBError
if errors.As(err, &dbe) { /* inspect driver cause */ }

Try / catch

if err := store.DeleteWisps(ctx, ids, opts); err != nil {
    if strings.Contains(err.Error(), "delete: drop wisp deps:") { /* retry after checking DB health */ }
}

Prevention

When it happens

Trigger: Deleting wisps (DeleteWisp/DeleteWisps or mixed sets) when the wisps dependency table is missing, locked, or the driver/SQL layer returns an error on DELETE; also connection or context failure mid-delete.

Common situations: Embedded Dolt DB where the wisps dependency table was never created (older schema); concurrent writer holding row locks; transient connection loss during a large wisp batch delete.

Related errors


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