{"record":{"id":"b37cf3a10885082b","repo":"gastownhall/beads","slug":"delete-wisp-s-w","errorCode":null,"errorMessage":"delete wisp %s: %w","messagePattern":"delete wisp (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":304,"sourceCode":"\t}\n\n\t// Resolve WHICH regular ids this delete actually removes before the batched\n\t// DELETE runs: afterwards the rows are gone, and RowsAffected reports a\n\t// count, not a set. A journal record for an id that was already absent would\n\t// tell a consumer to drop a bead this transaction never touched.\n\tjournaledDeletes, err := journalableDeletesInTx(ctx, tx, \"issues\", set.RegularIDs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Edges are journaled before the rows go, while their source snapshots can\n\t// still be read.\n\tif err := RecordDependencyRemovalsForIssuesInTx(ctx, tx, set.All); err != nil {\n\t\treturn nil, fmt.Errorf(\"journal dependency removals for batch delete: %w\", err)\n\t}\n\n\tfor _, id := range set.WispIDs {\n\t\tif err := deleteIssueRowInTx(ctx, tx, id, true); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"delete wisp %s: %w\", id, err)\n\t\t}\n\t}\n\n\ttotalRegularsDeleted := 0\n\tfor i := 0; i < len(set.RegularIDs); i += deleteBatchSize {\n\t\tend := i + deleteBatchSize\n\t\tif end > len(set.RegularIDs) {\n\t\t\tend = len(set.RegularIDs)\n\t\t}\n\t\tbatch := set.RegularIDs[i:end]\n\t\tbatchInClause, batchArgs := buildSQLInClause(batch)\n\n\t\tdeleteResult, err := tx.ExecContext(ctx,\n\t\t\tfmt.Sprintf(`DELETE FROM issues WHERE id IN (%s)`, batchInClause),\n\t\t\tbatchArgs...)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"delete issues: %w\", err)\n\t\t}","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L286-L322","documentation":"DeleteResolvedSetInTx wraps failures from deleteIssueRowInTx when removing each wisp row individually inside the delete transaction. Wisps are deleted one row at a time (rather than batched) because each wisp delete also journals itself. The wrapped error is whatever the underlying row-delete failed with (SQL error, context cancellation, etc.).","triggerScenarios":"Calling DeleteIssuesInTx or DeleteInTx (e.g. `bd delete --cascade` or bulk delete) where the resolved deletion set includes wisp IDs and the per-wisp DELETE fails — DB constraint violation, connection drop, or context timeout mid-loop.","commonSituations":"Deleting a large resolved set against a Dolt/SQLite database when the connection drops mid-transaction; running deletes with a short context deadline; schema drift where the wisps table lacks an expected column.","solutions":["Inspect the wrapped inner error for the real cause (SQL syntax/constraint/connection)","Retry the delete once the DB connection is healthy; the transaction rolls back atomically","Increase the context timeout for large cascade deletes","Verify schema is current (run bd's migration/doctor) if the inner error indicates a missing column/table"],"exampleFix":"// before\nif err := deleteIssueRowInTx(ctx, tx, id, true); err != nil {\n\treturn nil, fmt.Errorf(\"delete wisp %s: %w\", id, err)\n}\n// after — same code; fix the cause by giving the operation a generous deadline\nctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\ndefer cancel()","handlingStrategy":"retry","validationCode":"// check DB reachability and schema before deleting\nif err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"db unreachable: %w\", err) }","typeGuard":null,"tryCatchPattern":"if _, err := DeleteIssuesInTx(ctx, tx, ids, opts); err != nil {\n\tvar wrappedErr error\n\tif strings.Contains(err.Error(), \"delete wisp \") {\n\t\t// extract wisp id and inner cause; retry with fresh tx\n\t\twrappedErr = retryDelete(ctx, ids)\n\t}\n\treturn wrappedErr\n}","preventionTips":["Use a context timeout proportional to delete-set size","Keep the DB connection healthy (ping before long batch operations)","Keep schema migrated so wisp deletes do not hit missing columns","Avoid concurrent writers during bulk deletes"],"tags":["database","delete","transaction","wisps"],"backgroundTag":"bulk-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}