gastownhall/beads · error

delete: drop labels

Error message

delete: drop labels: %w

What it means

This error means deleting the label rows attached to the regular issues being deleted failed. During deleteMany's cascade, labelRepo.DeleteAllForIDs runs for regular IDs after dependencies are dropped; any storage error aborts the delete so labels and issues stay consistent.

Solutions

  1. Confirm the labels table exists and the DB user can delete from it.
  2. Retry the delete; the transactional cascade leaves no partial label removal.
  3. Inspect the wrapped driver error for the precise cause.
  4. Run schema migrations / bd doctor if the schema is stale.
Defensive patterns

Strategy: validation

Validate before calling

if err := ctx.Err(); err != nil { return err }
exists, err := tableExists(ctx, "labels"); if err != nil || !exists { return fmt.Errorf("labels table missing") }

Type guard

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

Try / catch

if err := store.DeleteIssues(ctx, ids, opts); err != nil {
    if strings.Contains(err.Error(), "delete: drop labels:") { /* check DB perms/retry */ }
}

Prevention

When it happens

Trigger: DeleteIssue/DeleteIssues calls where the labels table is missing, locked, or the driver returns an SQL error on DELETE; context cancellation; connection loss mid-cascade.

Common situations: Schema not migrated (labels table absent in embedded DB); DB user lacks DELETE privilege; lock contention with a concurrent bd process.

Related errors


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

Appendix: source

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

	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)
	}
	if _, err := u.eventsRepo.DeleteAllForIDs(ctx, regularIDs, RecordEventOpts{}); err != nil {
		return result, fmt.Errorf("delete: drop events: %w", err)
	}
	if _, err := u.eventsRepo.DeleteAllForIDs(ctx, wispIDs, RecordEventOpts{UseWispsTable: true}); err != nil {
		return result, fmt.Errorf("delete: drop wisp events: %w", err)
	}

	issuesDeleted, err := u.issueRepo.DeleteByIDs(ctx, regularIDs, IssueTableOpts{})
	if err != nil {
		return result, fmt.Errorf("delete: drop issue rows: %w", err)
	}
	wispsDeleted, err := u.issueRepo.DeleteByIDs(ctx, wispIDs, IssueTableOpts{UseWispsTable: true})
	if err != nil {
		return result, fmt.Errorf("delete: drop wisp rows: %w", err)

View on GitHub (pinned to 71377f2769)