gastownhall/beads · error
db: LabelSQLRepository.Insert %s/%s: rows affected: %w
Error message
db: LabelSQLRepository.Insert %s/%s: rows affected: %w
What it means
Thrown when result.RowsAffected() fails after the INSERT IGNORE in LabelSQLRepository.Insert. The driver failed to report how many rows the insert touched, so the repository cannot tell whether the label was actually added and cannot safely journal the EventLabelAdded event.
Source
Thrown at internal/storage/domain/db/label.go:60
// Reject an over-length label before the INSERT IGNORE, which would otherwise
// silently truncate it to the VARCHAR(255) column. This is the proxied-server
// (uow) analog of issueops.AddLabelInTx's guard, so both write stacks return a
// typed ErrFieldTooLong instead of storing a label the caller never sent.
if err := types.CheckFieldLen("label", label); err != nil {
return err
}
table := pickLabelTable(opts.UseWispsTable)
//nolint:gosec // G201: table is one of two hardcoded constants
result, err := r.runner.ExecContext(ctx,
fmt.Sprintf("INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)", table),
issueID, label,
)
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: %w", issueID, label, err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: rows affected: %w", issueID, label, err)
}
if rows == 0 {
issueTable := "issues"
if opts.UseWispsTable {
issueTable = "wisps"
}
var count int
//nolint:gosec // G201: issueTable is one of two hardcoded constants.
if err := r.runner.QueryRowContext(ctx, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE id = ?", issueTable), issueID).Scan(&count); err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: verify issue: %w", issueID, label, err)
}
if count == 0 {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: issue does not exist", issueID, label)
}
return nil
}
if err := r.events.Record(ctx, domain.Event{
IssueID: issueID,View on GitHub (pinned to 71377f2769)
Solutions
- Retry the insert; transient connection drops often recover
- Check DB connectivity and reconnect the pool
- Verify the Dolt driver version is current
- If in tests, ensure the mock result implements RowsAffected
Example fix
// before
rows, err := result.RowsAffected()
if err != nil { return fmt.Errorf("... rows affected: %w", err) }
// after
db := sqlx.NewDb(conn, "mysql"); db.SetConnMaxLifetime(30 * time.Second) // avoid stale connections
rows, err := result.RowsAffected() Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) } Try / catch
err := repo.Insert(ctx, issueID, label, opts)
for i := 0; i < 3 && err != nil; i++ {
time.Sleep(backoff(i))
err = repo.Insert(ctx, issueID, label, opts)
} Prevention
- Set ConnMaxLifetime to recycle stale pooled connections
- Retry idempotent inserts on transient driver errors
- Keep the Dolt driver up to date
- Avoid long-lived idle connections behind NAT/firewalls
When it happens
Trigger: Calling Insert against a driver/connection that errors on RowsAffected (e.g. broken connection returned before metadata, or a driver wrapper that does not implement RowsAffected for INSERT IGNORE results).
Common situations: Stale pooled connection that died between ExecContext and RowsAffected; mismatched or buggy driver version; result object from a mock/test double lacking RowsAffected support.
Related errors
- db: RawSQL Exec: rows affected: %w
- failed to get 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/bd8f339e1a5f0053.
Report an issue: GitHub.