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

  1. Retry the delete after reconnecting
  2. Update/verify the Dolt SQL driver version
  3. Fix test mocks to implement RowsAffected properly
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/4f9d12b08e962185. Report an issue: GitHub.