gastownhall/beads · error
db: LabelSQLRepository.Delete %s/%s: rows affected: %w
Error message
db: LabelSQLRepository.Delete %s/%s: rows affected: %w
What it means
Thrown when result.RowsAffected() fails after the DELETE statement in LabelSQLRepository.Delete. Without the affected-row count the repository cannot decide whether to journal the label-removed event, so it aborts with the driver error wrapped.
Source
Thrown at internal/storage/domain/db/label.go:108
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)
}
func (r *labelSQLRepositoryImpl) List(ctx context.Context, issueID string, opts domain.LabelOpts) ([]string, error) {
if issueID == "" {
return nil, fmt.Errorf("db: LabelSQLRepository.List: issueID must not be empty")View on GitHub (pinned to 71377f2769)
Solutions
- Retry the delete after reconnecting
- Update/verify the Dolt SQL driver version
- Fix test mocks to implement RowsAffected properly
- Check connection pool health settings
Example fix
// before db.SetMaxOpenConns(10) // stale connections linger // after db.SetConnMaxLifetime(30 * time.Second) db.SetConnMaxIdleTime(1 * time.Minute)
Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) } Try / catch
err := repo.Delete(ctx, issueID, label, actor, opts)
for i := 0; i < 3 && err != nil; i++ {
time.Sleep(backoff(i))
err = repo.Delete(ctx, issueID, label, actor, opts)
} Prevention
- Set ConnMaxLifetime/ConnMaxIdleTime on the pool
- Retry idempotent deletes on transient errors
- Implement RowsAffected correctly in test mocks
- Keep the SQL driver current
When it happens
Trigger: Delete called on a connection whose driver errors retrieving affected rows after DELETE (stale connection, driver limitation, or mock result in tests lacking RowsAffected).
Common situations: Pooled connection died between ExecContext and RowsAffected; test doubles returning sql.Result implementations without RowsAffected support; driver bug in an old version.
Related errors
- failed to check rows affected for issue counter prefix %q: %
- failed to check rows affected after seeding for prefix %q: %
- db: DependencySQLRepository.DeleteAllForIDs rows affected: %
- db: EventsSQLRepository.DeleteAllForIDs rows affected: %w
- db: Update %s: rows affected: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4f9d12b08e962185.
Report an issue: GitHub.