{"record":{"id":"f646d979eb7fbd60","repo":"gastownhall/beads","slug":"delete-leases-w-f646d9","errorCode":null,"errorMessage":"delete leases: %w","messagePattern":"delete leases: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":330,"sourceCode":"\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}\n\t\trowsAffected, _ := deleteResult.RowsAffected()\n\t\ttotalRegularsDeleted += int(rowsAffected)\n\n\t\t// Deleted issues hold no leases.\n\t\tif _, err := tx.ExecContext(ctx,\n\t\t\tfmt.Sprintf(`DELETE FROM leases WHERE issue_id IN (%s)`, batchInClause),\n\t\t\tbatchArgs...); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"delete leases: %w\", err)\n\t\t}\n\t}\n\tresult.DeletedCount = totalRegularsDeleted + len(set.WispIDs)\n\n\t// Journal every regular issue this bulk/cascade delete removed. Wisps went\n\t// through deleteIssueRowInTx above, which journals each itself; set.All is\n\t// cascade-expanded, so this records cascade deletes too. The delete\n\t// plumbing carries no actor, so the rows record none.\n\tfor _, id := range journaledDeletes {\n\t\tif err := RecordDeleteInTx(ctx, tx, id, \"\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {\n\t\treturn nil, fmt.Errorf(\"recompute is_blocked after batch delete: %w\", err)\n\t}\n","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L312-L348","documentation":"After deleting the issue rows, DeleteResolvedSetInTx removes any leases referencing the deleted issues; failure of `DELETE FROM leases WHERE issue_id IN (...)` is wrapped with this message. It exists to keep lease rows from orphaning after issues disappear.","triggerScenarios":"DeleteIssuesInTx/DeleteInTx where the issues DELETE succeeded but the follow-up leases DELETE fails — leases table missing (older schema), lock contention, or connection failure mid-transaction.","commonSituations":"Running a new binary against an old database where the leases table does not exist; concurrent agent holding leases causing lock contention on SQLite/Dolt.","solutions":["Check the wrapped error; if it is 'no such table: leases', migrate the database schema","Retry after the competing writer releases its lock / raise busy timeout","Verify connection stability for remote Dolt deployments"],"exampleFix":"// before\nif _, err := tx.ExecContext(ctx, fmt.Sprintf(`DELETE FROM leases WHERE issue_id IN (%s)`, batchInClause), batchArgs...); err != nil {\n\treturn nil, fmt.Errorf(\"delete leases: %w\", err)\n}\n// after — migrate first when the table is absent\n// bd doctor / apply migrations before retrying the delete","handlingStrategy":"try-catch","validationCode":"// confirm the leases table exists before deleting\nvar n int\nif err := db.QueryRow(\"SELECT COUNT(*) FROM sqlite_master WHERE name='leases'\").Scan(&n); err != nil || n == 0 {\n\treturn fmt.Errorf(\"leases table missing; run migrations\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := DeleteIssuesInTx(ctx, tx, ids, opts); err != nil {\n\tif strings.Contains(err.Error(), \"delete leases: \") && strings.Contains(err.Error(), \"no such table\") {\n\t\tmigrate(db) // then retry\n\t}\n\treturn err\n}","preventionTips":["Run migrations before any delete operation","Avoid holding leases on issues scheduled for deletion (release leases first)","Use non-interactive single-writer windows for bulk deletes"],"tags":["database","delete","leases","transaction"],"backgroundTag":"sql-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}