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, nilView on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error; if the connection was lost, reconnect and rerun (delete is idempotent for already-deleted rows)
- If the driver never supports RowsAffected here, track counts via a SELECT COUNT before delete or accept approximate totals
- 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
- Test driver/proxy RowsAffected behavior in CI
- Avoid proxies that strip OK-packet metadata
- Prefer explicit COUNT queries when exact counts matter
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
- 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: Update %s: rows affected: %w
- db: Claim %s: rows affected: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/636c3425d4f99654.
Report an issue: GitHub.