{"record":{"id":"a125074089800bee","repo":"gastownhall/beads","slug":"delete-drop-deps-w","errorCode":null,"errorMessage":"delete: drop deps: %w","messagePattern":"delete: drop deps: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue_delete.go","lineNumber":167,"sourceCode":"\tvar connectedIsWisp map[string]bool\n\tif params.UpdateTextReferences {\n\t\tdeletedSet := make(map[string]bool, len(allIDs))\n\t\tfor _, id := range allIDs {\n\t\t\tdeletedSet[id] = true\n\t\t}\n\t\tconnected, connectedIsWisp, err = u.collectConnectedIssues(ctx, allIDs, deletedSet)\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\t}\n\n\taffectedIssues, affectedWisps, err := u.issueRepo.AffectedByDeletion(ctx, regularIDs, wispIDs)\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"delete: affected by deletion: %w\", err)\n\t}\n\n\tif _, err := u.depRepo.DeleteAllForIDs(ctx, regularIDs, DepInsertOpts{}); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop deps: %w\", err)\n\t}\n\tif _, err := u.depRepo.DeleteAllForIDs(ctx, wispIDs, DepInsertOpts{UseWispsTable: true}); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop wisp deps: %w\", err)\n\t}\n\t// The SYNC-PLANE edges pointing at a deleted wisp, which are not the same\n\t// rows as the line above and are not reached by a foreign key: there is no\n\t// FK from dependencies to wisps, so `dependencies.depends_on_wisp_id` rows\n\t// survive their target unless they are deleted explicitly. Without this a\n\t// forced delete of a wisp left its durable dependent holding an edge into\n\t// a row that no longer exists — dangling, not orphaned, which is not what\n\t// issueops.DeleteRequest.Force promises. The store body has always done\n\t// this (issueops.deleteIssueRowInTx -> DeleteWispFromDependenciesInTx).\n\tif _, err := u.depRepo.DeleteAllForIDs(ctx, wispIDs, DepInsertOpts{}); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop sync-plane edges into deleted wisps: %w\", err)\n\t}\n\tif _, err := u.labelRepo.DeleteAllForIDs(ctx, regularIDs, LabelOpts{}); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop labels: %w\", err)\n\t}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue_delete.go#L149-L185","documentation":"This error wraps a failure while deleting dependency rows that originate from the regular issues being deleted. During deleteMany, after computing affected issues, the use case calls depRepo.DeleteAllForIDs for regular IDs; any storage-layer failure (Dolt/SQL error, connection loss, table missing) surfaces here prefixed 'delete: drop deps:'. It means the cascade delete of dependency edges for non-wisp issues could not complete and the whole delete aborts.","triggerScenarios":"Calling DeleteIssue/DeleteIssues (or the mixed DeleteWisps paths with regular IDs present) when the underlying dependencies table is unreadable/unwritable, the DB connection drops mid-transaction, or the driver returns an SQL error on DELETE.","commonSituations":"Database unavailable or restarted mid-operation; schema migration missed so the dependencies table does not exist; permission denied on the DB user; context cancelled during a long batch delete.","solutions":["Check DB connectivity and that the 'dependencies' table exists in the current schema (run bd doctor / schema migration).","Retry the delete; the operation is transaction-scoped so no partial dependency rows are left behind.","Inspect the wrapped cause (%w chain) for the specific driver error (deadlock, lock wait timeout, permission) and fix that.","If running against an embedded Dolt database, verify no competing process holds conflicting locks.","Ensure context deadlines are long enough for large ID batches."],"exampleFix":"// before\nerr := store.DeleteIssues(ctx, ids, opts) // opaque 'delete: drop deps' failure\n// after\nif err := ctx.Err(); err != nil { return fmt.Errorf(\"ctx cancelled before delete: %w\", err) }\nif err := pingDB(ctx); err != nil { return fmt.Errorf(\"db not reachable: %w\", err) }\nerr := store.DeleteIssues(ctx, ids, opts)","handlingStrategy":"validation","validationCode":"if err := ctx.Err(); err != nil { return err }\nif err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"db unreachable: %w\", err) }\nexists, err := tableExists(ctx, \"dependencies\"); if err != nil || !exists { return fmt.Errorf(\"dependencies table missing\") }","typeGuard":"var dbe *storage.DBError\nif errors.As(err, &dbe) { /* inspect dbe.Cause for driver-level cause */ }","tryCatchPattern":"if err := store.DeleteIssues(ctx, ids, opts); err != nil {\n    var blocked *domain.DeleteBlockedError\n    if errors.As(err, &blocked) { /* handle blocked delete */ }\n    if strings.Contains(err.Error(), \"delete: drop deps:\") { /* retry or report DB fault */ }\n}","preventionTips":["Keep schema migrations current for the embedded Dolt store.","Set generous context timeouts for batch deletes.","Verify DB connectivity before large delete operations.","Avoid running concurrent writers against the same database file."],"tags":["storage","database","delete","dependencies"],"backgroundTag":"dependency-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}