gastownhall/beads · warning

db: LabelSQLRepository.DeleteAllForIDs rows affected: %w

Error message

db: LabelSQLRepository.DeleteAllForIDs rows affected: %w

What it means

LabelSQLRepository.DeleteAllForIDs executed the DELETE but res.RowsAffected() returned an error, so the number of deleted rows could not be determined. This is a driver capability/transport issue, not a failed delete.

Source

Thrown at internal/storage/domain/db/label.go:222

		placeholders := make([]string, len(batch))
		args := make([]any, len(batch))
		for i, id := range batch {
			placeholders[i] = "?"
			args[i] = id
		}
		//nolint:gosec // G201: table is one of two hardcoded constants; ? placeholders only.
		res, err := r.runner.ExecContext(ctx,
			fmt.Sprintf("DELETE FROM %s WHERE issue_id IN (%s)", table, strings.Join(placeholders, ",")),
			args...)
		if err != nil {
			if opts.UseWispsTable && dberrors.IsTableNotExist(err) {
				return total, nil
			}
			return total, fmt.Errorf("db: LabelSQLRepository.DeleteAllForIDs from %s: %w", table, err)
		}
		n, err := res.RowsAffected()
		if err != nil {
			return total, fmt.Errorf("db: LabelSQLRepository.DeleteAllForIDs rows affected: %w", err)
		}
		total += int(n)
	}
	return total, nil
}

func (r *labelSQLRepositoryImpl) CountAllForIDs(ctx context.Context, ids []string, opts domain.LabelOpts) (int, error) {
	if len(ids) == 0 {
		return 0, nil
	}
	table := "labels"
	if opts.UseWispsTable {
		table = "wisp_labels"
	}
	count, err := issueops.CountRowsForIssueIDsInTx(ctx, r.runner, table, ids)
	if err != nil {
		if opts.UseWispsTable && dberrors.IsTableNotExist(err) {
			return 0, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to see which driver call failed
  2. Check the storage driver supports RowsAffected for DML (upgrade driver if not)
  3. If using a mock/test runner, implement RowsAffected in the stub result
  4. Treat total as best-effort only if your use case does not need exact counts (may require upstream change)

Example fix

// before
// stub result without RowsAffected
type fakeResult struct{}
// after
type fakeResult struct{ n int64 }
func (f fakeResult) RowsAffected() (int64, error) { return f.n, nil }
func (f fakeResult) LastInsertId() (int64, error) { return 0, nil }
Defensive patterns

Strategy: fallback

Validate before calling

// prefer drivers known to support RowsAffected; avoid stub runners in prod paths

Try / catch

n, err := repo.DeleteAllForIDs(ctx, ids, opts)
if err != nil && strings.Contains(err.Error(), "rows affected") {
    // delete likely succeeded; treat count as unknown
    log.Printf("delete ok, count unknown: %v", err)
    return nil
}

Prevention

When it happens

Trigger: Calling DeleteAllForIDs against a driver/connection that does not support or fails to report rows affected (e.g. some drivers return driver.ErrSkip or transport errors).

Common situations: Using a driver or proxy that does not implement RowsAffected; connection interruption right after statement execution; mock/stub runners in tests lacking RowsAffected support.

Related errors


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