gastownhall/beads · warning

db: EventsSQLRepository.DeleteAllForIDs rows affected: %w

Error message

db: EventsSQLRepository.DeleteAllForIDs rows affected: %w

What it means

Wraps failures from res.RowsAffected() after a successful DELETE in DeleteAllForIDs. Some drivers can fail to report affected-row counts (e.g. unsupported or lost connection before metadata returns). The delete itself succeeded; only the count retrieval failed.

Source

Thrown at internal/storage/domain/db/events.go:67

		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: EventsSQLRepository.DeleteAllForIDs from %s: %w", table, err)
		}
		n, err := res.RowsAffected()
		if err != nil {
			return total, fmt.Errorf("db: EventsSQLRepository.DeleteAllForIDs rows affected: %w", err)
		}
		total += int(n)
	}
	return total, nil
}

func (r *eventsSQLRepositoryImpl) CountAllForIDs(ctx context.Context, ids []string, opts domain.RecordEventOpts) (int, error) {
	if len(ids) == 0 {
		return 0, nil
	}
	table := "events"
	if opts.UseWispsTable {
		table = "wisp_events"
	}
	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. Check the wrapped error; if the connection was lost, reconnect and rerun (delete is idempotent for already-deleted rows)
  2. If the driver never supports RowsAffected here, track counts via a SELECT COUNT before delete or accept approximate totals
  3. Test against the exact Dolt/driver version in use; upgrade if a known RowsAffected bug exists

Example fix

// before
n, err := res.RowsAffected()
if err != nil { return total, err }
// after
n, rerr := res.RowsAffected()
if rerr != nil {
    log.Warnf("rows affected unavailable: %v", rerr)
    n = 0 // treat as unknown, continue
}
Defensive patterns

Strategy: fallback

Validate before calling

// no pre-call validation possible; failure occurs after successful delete

Type guard

func isRowsAffectedUnsupported(err error) bool {
    return err != nil && (errors.Is(err, go_mysql.ErrSkip) || strings.Contains(err.Error(), "RowsAffected"))
}

Try / catch

deleted, err := repo.DeleteAllForIDs(ctx, ids, opts)
if err != nil {
    if isRowsAffectedUnsupported(err) {
        log.Warnf("row count unavailable; treat as unknown: %v", err)
        return nil // delete itself succeeded
    }
    return err
}

Prevention

When it happens

Trigger: Calling DeleteAllForIDs against a driver/server that doesn't populate RowsAffected for the executed statement, or whose connection dropped between DELETE completion and row-count retrieval.

Common situations: Proxy or pooler (e.g. some MySQL proxies) stripping OK-packet metadata; driver quirks with multi-statement or prepared DELETEs; connection reset right after execute.

Related errors


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