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
- Verify the wisps dependency table exists for your schema version; run migrations/doctor.
- Retry the delete; the transactional scope prevents partial state.
- Read the wrapped driver error for the root cause (lock timeout, permissions) and address it.
- 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
- Run bd doctor to verify embedded schema includes wisp tables.
- Migrate old databases created before wisp-plane tables existed.
- Chunk large wisp deletions.
- Use a single writer process per database.
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
- delete: drop deps: %w
- delete: drop sync-plane edges into deleted wisps: %w
- delete: drop wisp rows: %w
- hydrate neighbors (wisps): %w
- delete: count deps: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9045d9676f4257f8.
Report an issue: GitHub.