gastownhall/beads · error
db: LabelSQLRepository.Delete %s/%s: %w
Error message
db: LabelSQLRepository.Delete %s/%s: %w
What it means
Wraps any failure of the DELETE FROM statement in LabelSQLRepository.Delete, adding issue ID and label context. Thrown when the SQL driver rejects the delete (connection failure, schema problem, permissions), so the label removal did not happen and no event was journaled.
Source
Thrown at internal/storage/domain/db/label.go:104
// returns without writing and journals nothing.
return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)
}
func (r *labelSQLRepositoryImpl) Delete(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {
if issueID == "" {
return fmt.Errorf("db: LabelSQLRepository.Delete: issueID must not be empty")
}
if label == "" {
return fmt.Errorf("db: LabelSQLRepository.Delete: label must not be empty")
}
table := pickLabelTable(opts.UseWispsTable)
//nolint:gosec // G201: table is one of two hardcoded constants
result, err := r.runner.ExecContext(ctx,
fmt.Sprintf("DELETE FROM %s WHERE issue_id = ? AND label = ?", table),
issueID, label,
)
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Delete %s/%s: %w", issueID, label, err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Delete %s/%s: rows affected: %w", issueID, label, err)
}
if rows == 0 {
return nil
}
if err := r.events.Record(ctx, domain.Event{
IssueID: issueID,
Type: types.EventLabelRemoved,
Actor: actor,
OldValue: label,
}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
return err
}
return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error via errors.As
- Verify the labels table exists and is writable
- Check DB connectivity and credentials/privileges
- Retry if the failure was transient
Example fix
// before
repo.Delete(ctx, issueID, label, actor, opts)
// after
if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {
var drv *mysql.MySQLError
if errors.As(err, &drv) { log.Errorf("delete failed: %d %v", drv.Number, drv) }
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
if issueID == "" || label == "" { return fmt.Errorf("issueID and label required") }
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) } Type guard
func isSQLErr(err error) bool { var e *mysql.MySQLError; return errors.As(err, &e) } Try / catch
if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {
var sqle *mysql.MySQLError
if errors.As(err, &sqle) { log.Errorf("delete failed: %d %s", sqle.Number, sqle.Message) }
return fmt.Errorf("remove label %q from %s: %w", label, issueID, err)
} Prevention
- Ping DB before write operations
- Ensure migrations created the labels tables
- Grant DELETE privileges to the app DB user
- Wrap context with a sane timeout
When it happens
Trigger: Calling Delete when the DB connection is broken, the labels table is missing, or the user lacks DELETE privileges on the table.
Common situations: Database not migrated (labels table absent); read-only replica receiving writes; connection dropped mid-transaction.
Related errors
- failed to remove federation peer: %w
- db: LabelSQLRepository.DeleteAllForIDs from %s: %w
- delete issue from %s: %w
- delete issues: %w
- remove label: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/316ab6cfbd7aacef.
Report an issue: GitHub.