gastownhall/beads · error

failed to get rows affected: %w

Error message

failed to get rows affected: %w

What it means

After the claim UPDATE succeeds as SQL, ClaimIssueInTx calls result.RowsAffected() to decide whether the CAS won. If the driver cannot report the affected-row count, this error wraps that failure. It indicates a driver/driver-backend limitation rather than a claim conflict.

Source

Thrown at internal/storage/issueops/claim.go:134

				SET assignee = ?, status = 'in_progress', updated_at = ?, started_at = ?, %s
				WHERE id = ? AND row_lock = ? AND status IN (%s)
			`, issueTable, rowLockClause, statusPlaceholders), args...)
		} else {
			args := append([]interface{}{actor, now}, rowLockArgs...)
			args = append(args, id, oldIssue.RowVersion)
			args = append(args, statusArgs...)
			result, err = tx.ExecContext(ctx, fmt.Sprintf(`
				UPDATE %s
				SET assignee = ?, status = 'in_progress', updated_at = ?, %s
				WHERE id = ? AND row_lock = ? AND status IN (%s)
			`, issueTable, rowLockClause, statusPlaceholders), args...)
		}
		if err != nil {
			return nil, fmt.Errorf("failed to claim issue: %w", err)
		}
		rowsAffected, err = result.RowsAffected()
		if err != nil {
			return nil, fmt.Errorf("failed to get rows affected: %w", err)
		}
	}

	if rowsAffected == 0 {
		assignee, currentStatus, err := readClaimStateInTx(ctx, tx, issueTable, id)
		if err != nil {
			return nil, fmt.Errorf("failed to get current claim state: %w", err)
		}
		// Idempotent: if already claimed in_progress by the same actor —
		// including a spelling difference across layers (ga-wzl83) — treat as
		// success. This supports agent retry workflows where claim may be
		// called multiple times after transient failures (GH#8).
		if actorMatches(assignee, actor) && currentStatus == types.StatusInProgress {
			return &ClaimResult{OldIssue: oldIssue, IsWisp: isWisp}, nil
		}
		// The refusal carries the state that lost the CAS, read just above in
		// THIS transaction, so a caller learns who won without parsing the
		// message. The typed wrapper carries the fields; the PROSE is composed

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the claim; a same-actor in_progress re-claim is idempotent
  2. Verify the storage driver is the supported dolthub/driver path, not a shim
  3. Check connection stability to the database server
Defensive patterns

Strategy: retry

Validate before calling

// use the supported dolthub driver, not a shim; verify with bd doctor
// no pre-call check can detect RowsAffected support; treat as infra health issue

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to get rows affected") {
    return retryClaim(ctx, id, actor) // claim CAS is idempotent for same actor
}

Prevention

When it happens

Trigger: tx.ExecContext succeeded but result.RowsAffected() returned an error on the CAS UPDATE in ClaimIssueInTx — typically with drivers or proxy setups that don't implement RowsAffected, or a connection that dropped between exec and stat.

Common situations: Using a non-standard SQL driver or connection pooler in front of Dolt that doesn't propagate affected-row metadata; flaky connection dying between ExecContext and RowsAffected.

Related errors


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