gastownhall/beads · error
failed to check rows affected for issue counter prefix %q: %
Error message
failed to check rows affected for issue counter prefix %q: %w
What it means
After the counter UPDATE succeeds, nextCounterIDTx calls res.RowsAffected() to learn whether a counter row existed. This error wraps a failure of RowsAffected() itself — the Dolt driver could not report the affected-row count for the executed statement. It is a driver-capability/protocol problem, not a data problem.
Source
Thrown at internal/storage/dolt/issues.go:866
}
// nextCounterIDTx atomically increments and returns the next sequential issue ID
// for the given prefix within an existing transaction. Returns the full ID string
// (e.g., "bd-1"). Used by both generateIssueID and generateIssueIDInTable.
func nextCounterIDTx(ctx context.Context, tx *sql.Tx, prefix string) (string, error) {
// Increment atomically at the DB level to avoid duplicate IDs under
// concurrent transactions (GH#2002). "last_id = last_id + 1" is evaluated
// by the DB engine atomically within Dolt's MVCC.
// Attempt atomic increment of an existing counter row.
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
if err != nil {
return "", fmt.Errorf("failed to increment issue counter for prefix %q: %w", prefix, err)
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected for issue counter prefix %q: %w", prefix, err)
}
if rowsAffected == 0 {
// No counter row yet - seed from existing issues before proceeding to
// avoid collisions with manually-created sequential IDs (GH#2002).
if seedErr := seedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {
return "", fmt.Errorf("failed to seed issue counter for prefix %q: %w", prefix, seedErr)
}
// Retry the atomic increment after seeding.
res, err = tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
if err != nil {
return "", fmt.Errorf("failed to increment issue counter after seeding for prefix %q: %w", prefix, err)
}
rowsAffected, err = res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected after seeding for prefix %q: %w", prefix, err)
}
if rowsAffected == 0 {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error; upgrade the Dolt SQL driver and beads to matching, current versions.
- Bypass proxies/intermediaries between beads and the Dolt server to rule out metadata stripping.
- Retry the operation; if transient, check server logs for connection/protocol errors at that timestamp.
- As a fallback, upgrade to a beads version that treats RowsAffected errors via a read-back path instead of failing.
Example fix
// before
rowsAffected, err := res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected for issue counter prefix %q: %w", prefix, err)
}
// after (degrade gracefully by reading back the row)
rowsAffected, err := res.RowsAffected()
if err != nil {
var cur int
if scanErr := tx.QueryRowContext(ctx, "SELECT last_id FROM issue_counter WHERE prefix = ?", prefix).Scan(&cur); scanErr != nil {
return "", fmt.Errorf("failed to check rows affected for issue counter prefix %q: %w", prefix, err)
}
rowsAffected = 1
} Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
func isRowsAffectedErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "rows affected")
} Try / catch
id, err := store.CreateIssue(ctx, issue)
var wrappedErr error
if err != nil && errors.As(err, &wrappedErr) && strings.Contains(err.Error(), "rows affected for issue counter") {
// driver capability problem: upgrade driver/beads, then retry
return fmt.Errorf("driver cannot report rows affected; upgrade driver: %w", err)
} Prevention
- Pin compatible versions of the Dolt SQL driver and beads
- Do not route embedded DB traffic through proxies that strip result metadata
- Test counter-mode ID generation after any driver upgrade
- Report persistent RowsAffected failures to the driver maintainers
When it happens
Trigger: Calling generateIssueIDInTable in counter mode immediately after a successful UPDATE when the driver's RowsAffected() returns a non-nil error (driver version lacking result-metadata support, connection in a bad state after exec, server protocol mismatch).
Common situations: Using an outdated or third-party SQL driver for Dolt that doesn't implement affected-rows reporting for UPDATEs; running against a proxy/middleware that strips result metadata; driver version mismatch with the Dolt server.
Related errors
- failed to check rows affected after seeding for prefix %q: %
- db: DependencySQLRepository.DeleteAllForIDs rows affected: %
- db: EventsSQLRepository.DeleteAllForIDs rows affected: %w
- 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/5f4eb0d79212325f.
Report an issue: GitHub.