gastownhall/beads · error

get rows affected: %w

Error message

get rows affected: %w

What it means

deleteIssueRowInTx wraps a failure of result.RowsAffected() after the DELETE statement succeeded. This is rare: it means the driver could not report how many rows the statement removed (some drivers/pooled connections can't provide rows-affected). It precedes the not-found check.

Source

Thrown at internal/storage/issueops/delete.go:62

	}

	if err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {
		return fmt.Errorf("recompute is_blocked after delete for %s: %w", id, err)
	}

	return nil
}

//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)
func deleteIssueRowInTx(ctx context.Context, tx *sql.Tx, id string, isWisp bool) error {
	issueTable, _, _, _ := WispTableRouting(isWisp)
	result, err := tx.ExecContext(ctx, fmt.Sprintf("DELETE FROM %s WHERE id = ?", issueTable), id)
	if err != nil {
		return fmt.Errorf("delete issue from %s: %w", issueTable, err)
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("get rows affected: %w", err)
	}
	if rows == 0 {
		// Wrap the sentinel so callers can errors.Is(..., storage.ErrNotFound),
		// matching GetIssue/UpdateIssue. The storage conformance suite asserts
		// this parity across not-found paths.
		return fmt.Errorf("%w: issue %s", storage.ErrNotFound, id)
	}
	// Journal the delete in the same transaction. This worker backs single
	// deletes (DeleteIssueInTx) and the per-wisp branch of the bulk delete
	// (DeleteResolvedSetInTx); the bulk regular-issue branch journals its own
	// ids directly. The rows==0 return above is what keeps this
	// actually-deleted-only. The delete plumbing (storage.DeleteIssue and the
	// bulk/cascade resolvers) carries no actor, so the row records none.
	if err := RecordDeleteInTx(ctx, tx, id, ""); err != nil {
		return err
	}
	if isWisp {
		if err := DeleteWispFromDependenciesInTx(ctx, tx, id); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error; it is a driver result-metadata failure.
  2. Upgrade the dolthub/driver dependency to a current version.
  3. Retry the delete in a fresh transaction/connection.
  4. If reproducible, report to the driver: the DELETE succeeded, only the affected-count read failed.
Defensive patterns

Strategy: fallback

Try / catch

if err := storage.DeleteIssue(ctx, db, id); err != nil {
	if isResultMetadataErr(err) { // RowsAffected failed after a successful DELETE
		// verify outcome by re-reading the row, then proceed or retry
	}
	return err
}

Prevention

When it happens

Trigger: Calling any delete path when the database/driver's RowsAffected call errors after a successful DELETE — driver-level result metadata failure, very old or mismatched driver version.

Common situations: Mismatched or outdated Dolt SQL driver version that doesn't fully implement result metadata; connection in a degraded state where the result handle is invalid.

Related errors


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