gastownhall/beads · warning

rows affected: %w

Error message

rows affected: %w

What it means

After the DELETE, result.RowsAffected() is read to report how many issues were removed; an error there is wrapped as "rows affected: %w". The delete itself succeeded, but the affected-row count could not be retrieved.

Source

Thrown at internal/storage/issueops/bulk_ops.go:220

	if _, err := tx.ExecContext(ctx,
		`DELETE FROM leases WHERE issue_id IN (SELECT id FROM issues WHERE source_repo = ?)`, sourceRepo); err != nil {
		return 0, fmt.Errorf("delete leases: %w", err)
	}

	// Edges are journaled before the rows go, while their source snapshots can
	// still be read.
	if err := RecordDependencyRemovalsForIssuesInTx(ctx, tx, issueIDs); err != nil {
		return 0, fmt.Errorf("journal dependency removals for source-repo delete: %w", err)
	}

	result, err := tx.ExecContext(ctx, `DELETE FROM issues WHERE source_repo = ?`, sourceRepo)
	if err != nil {
		return 0, fmt.Errorf("delete issues: %w", err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return 0, fmt.Errorf("rows affected: %w", err)
	}

	// Journal each deleted issue in the same transaction. issueIDs is the exact
	// set removed by the DELETE above (both were scoped to source_repo), so
	// there are no phantom records here. The source-repo bulk delete plumbing
	// carries no actor, so the rows record none.
	for _, id := range issueIDs {
		if err := RecordDeleteInTx(ctx, tx, id, ""); err != nil {
			return int(rowsAffected), err
		}
	}

	if err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {
		return int(rowsAffected), fmt.Errorf("recompute is_blocked after source-repo delete: %w", err)
	}

	return int(rowsAffected), nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped error and which driver is configured; switch to a driver that supports RowsAffected
  2. Treat the delete as successful and tolerate a missing count if the count is only informational
  3. Pin to the standard, supported database driver for bd
Defensive patterns

Strategy: fallback

Validate before calling

// verify driver supports RowsAffected with a trivial statement
res, err := db.Exec("SELECT 1")
_, rerr := res.RowsAffected() // fail fast at startup if unsupported

Type guard

var unsupported interface{ Error() string }
_ = errors.As(err, &unsupported) // driver-specific RowsAffected limitation

Try / catch

n, err := DeleteIssuesBySourceRepoInTx(ctx, tx, repo)
if err != nil && strings.Contains(err.Error(), "rows affected") {
    // deletion succeeded; degrade gracefully by treating the count as unknown
    log.Warn("could not read affected-row count", "err", err)
    return nil
}

Prevention

When it happens

Trigger: RowsAffected returns an error, which is rare and driver-specific: some drivers cannot report affected rows for certain statements or connection states.

Common situations: Using a driver or middleware wrapper that does not implement RowsAffected; exotic storage backends behind the same sql.Tx interface.

Related errors


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