{"record":{"id":"49fbfc7bdf92096c","repo":"gastownhall/beads","slug":"delete-issue-from-s-w","errorCode":null,"errorMessage":"delete issue from %s: %w","messagePattern":"delete issue from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":58,"sourceCode":"\t\treturn fmt.Errorf(\"journal dependency removals for %s: %w\", id, err)\n\t}\n\tif err := deleteIssueRowInTx(ctx, tx, id, isWisp); err != nil {\n\t\treturn err\n\t}\n\n\tif err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {\n\t\treturn fmt.Errorf(\"recompute is_blocked after delete for %s: %w\", id, err)\n\t}\n\n\treturn nil\n}\n\n//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)\nfunc deleteIssueRowInTx(ctx context.Context, tx *sql.Tx, id string, isWisp bool) error {\n\tissueTable, _, _, _ := WispTableRouting(isWisp)\n\tresult, err := tx.ExecContext(ctx, fmt.Sprintf(\"DELETE FROM %s WHERE id = ?\", issueTable), id)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"delete issue from %s: %w\", issueTable, err)\n\t}\n\trows, err := result.RowsAffected()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get rows affected: %w\", err)\n\t}\n\tif rows == 0 {\n\t\t// Wrap the sentinel so callers can errors.Is(..., storage.ErrNotFound),\n\t\t// matching GetIssue/UpdateIssue. The storage conformance suite asserts\n\t\t// this parity across not-found paths.\n\t\treturn fmt.Errorf(\"%w: issue %s\", storage.ErrNotFound, id)\n\t}\n\t// Journal the delete in the same transaction. This worker backs single\n\t// deletes (DeleteIssueInTx) and the per-wisp branch of the bulk delete\n\t// (DeleteResolvedSetInTx); the bulk regular-issue branch journals its own\n\t// ids directly. The rows==0 return above is what keeps this\n\t// actually-deleted-only. The delete plumbing (storage.DeleteIssue and the\n\t// bulk/cascade resolvers) carries no actor, so the row records none.\n\tif err := RecordDeleteInTx(ctx, tx, id, \"\"); err != nil {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L40-L76","documentation":"deleteIssueRowInTx wraps the SQL error from the DELETE statement itself, with the routed table name (issues vs the wisp table) interpolated — table names are hardcoded constants via WispTableRouting. This is a driver/SQL-level failure, not a 'row not found' case (that is a separate sentinel error).","triggerScenarios":"Any call into DeleteIssue/DeleteIssues/DeleteResolvedSetInTx where the DELETE ... WHERE id = ? statement returns a driver error: bad connection, table missing, constraint violation from a foreign key not cleaned up, read-only replica.","commonSituations":"Database file corruption or a stale schema where the routed table doesn't exist; deleting an issue whose dependencies rows were created outside the tx by another process with FK enforcement; disk-full on the Dolt data directory.","solutions":["Read the wrapped SQL error for the concrete driver message (table, constraint, connection).","Run schema migration if the routed table is missing (stale schema after upgrade).","Check disk space and DB health if the message indicates I/O or corruption.","Retry in a fresh transaction if the failure was a dropped connection; never reuse a poisoned tx."],"exampleFix":"// before: ignoring schema drift after a version upgrade\nerr := storage.DeleteIssue(ctx, db, id) // fails: no such table: issues\n// after: ensure migrations run before storage use\nif err := bd.Migrate(db); err != nil { return err }\nerr = storage.DeleteIssue(ctx, db, id)","handlingStrategy":"validation","validationCode":"// pre-flight: table exists and connection healthy\nvar one int\nif err := db.QueryRowContext(ctx, \"SELECT 1\").Scan(&one); err != nil { return err }\nif err := bd.MigrateIfStale(ctx, db); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := storage.DeleteIssue(ctx, db, id); err != nil {\n\tif errors.Is(err, storage.ErrNotFound) { return nil } // not this error; see not-found\n\treturn fmt.Errorf(\"delete failed: %w\", err) // surface the wrapped SQL cause\n}","preventionTips":["Run schema migrations after every beads upgrade.","Monitor disk space on the Dolt data directory.","Never target read replicas for writes.","Retry only on fresh transactions after driver.ErrBadConn."],"tags":["database","sql","delete","error-wrapping"],"backgroundTag":"sql-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}