gastownhall/beads · error
failed to get rows affected: %w
Error message
failed to get rows affected: %w
What it means
This error is returned when result.RowsAffected() — used to detect whether the close UPDATE actually matched a row — fails at the driver level. Beads relies on the affected-row count to distinguish 'closed' from 'issue not found / already closed', so a failure here aborts the close. The root driver error is preserved with %w.
Source
Thrown at internal/storage/issueops/close.go:350
now := time.Now().UTC()
// row_lock is rewritten on close so a concurrent reclaim (which also rewrites
// row_lock) collides on this cell and is forced to conflict-and-retry rather
// than silently cell-merging a revert-to-ready over a completed close (see
// lease.go). The lease row is deleted below: a closed issue holds no lease.
result, err := tx.ExecContext(ctx, fmt.Sprintf(`
UPDATE %s SET status = ?, closed_at = ?, updated_at = ?, close_reason = ?, closed_by_session = ?,
row_lock = ?
WHERE id = ? AND status != ?
`, issueTable), types.StatusClosed, now, now, reason, session, freshRowLock(), id, types.StatusClosed)
if err != nil {
return nil, fmt.Errorf("failed to close issue: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return nil, fmt.Errorf("failed to get rows affected: %w", err)
}
if rows == 0 {
var status string
qerr := tx.QueryRowContext(ctx,
fmt.Sprintf(`SELECT status FROM %s WHERE id = ?`, issueTable), id,
).Scan(&status)
if qerr == sql.ErrNoRows {
return nil, fmt.Errorf("%w: issue %s", storage.ErrNotFound, id)
}
if qerr != nil {
return nil, fmt.Errorf("failed to check issue existence: %w", qerr)
}
if types.Status(status) == types.StatusClosed {
return &CloseResult{IsWisp: isWisp, AlreadyClosed: true}, nil
}
return nil, fmt.Errorf("failed to close issue: %s", id)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Check which storage driver is in use; ensure it is the supported dolthub/driver interface and not a proxy that drops affected-rows metadata.
- Retry the close operation with a fresh transaction and connection.
- Update the driver/storage layer if running an old or patched driver version.
- If reproducible, capture the wrapped error and report it — this path should never fail with a healthy driver.
Defensive patterns
Strategy: retry
Validate before calling
// ensure a supported driver is configured
if !isSupportedStorageDriver(cfg.Driver) {
return fmt.Errorf("unsupported storage driver %q: RowsAffected may not be available", cfg.Driver)
} Try / catch
err := store.CloseIssue(ctx, id, actor, reason)
if err != nil && strings.Contains(err.Error(), "failed to get rows affected") {
// retry once; if persistent, driver is broken — report it
return retryClose(ctx, id, actor, reason, 1)
} Prevention
- Use the supported dolthub/driver storage interface, not proxies or custom drivers.
- Keep the driver updated to a version known to report affected rows.
- Treat repeated occurrences as a driver bug and file an issue with the wrapped error.
- Avoid layers that rewrite or intercept the wire protocol between beads and the database.
When it happens
Trigger: tx.ExecContext succeeded but result.RowsAffected() returns an error: unsupported by the driver for the statement type, driver/connection error after executing, or a driver implementation that cannot report rows affected.
Common situations: Using a driver or proxy (e.g. an unusual Dolt/MySQL proxy) that doesn't implement the affected-rows protocol correctly; connection reset right after the UPDATE; custom driver in the storage boundary that doesn't support RowsAffected.
Related errors
- db: LabelSQLRepository.Insert %s/%s: rows affected: %w
- db: RawSQL Exec: rows affected: %w
- failed to check rows affected for issue counter prefix %q: %
- failed to check rows affected after seeding for prefix %q: %
- failed to check rows affected for issue counter prefix %q: %
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/eb66f5e6476869c8.
Report an issue: GitHub.