gastownhall/beads · error

failed to claim issue: %w

Error message

failed to claim issue: %w

What it means

ClaimIssueInTx wraps any error returned by the database driver when executing the claim compare-and-swap UPDATE (setting assignee/status='in_progress' guarded on row_lock and claimable status). This means the SQL execution itself failed — not that the claim was refused — so the claim did not happen and the wrapped driver error carries the root cause (connection loss, syntax/constraint error, context cancellation).

Source

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

			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 = ?, 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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error (%w) for the root cause and fix it (reconnect, restart server, etc.)
  2. Retry the claim — the CAS is idempotent for the same actor once in_progress
  3. Check DB connectivity/schema version (bd doctor) if the failure is persistent

Example fix

// before
issued, err := ClaimIssueInTx(ctx, tx, id, actor) // raw failure surfaces here
// after
issued, err := ClaimIssueInTx(ctx, tx, id, actor)
if err != nil {
    var conflict *publicops.ClaimConflictError
    if !errors.As(err, &conflict) { // only transient/infra failures fall through
        time.Sleep(backoff); return ClaimIssueInTx(ctx, tx, id, actor)
    }
    return nil, err
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure connectivity before claiming
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) }

Try / catch

res, err := ClaimIssueInTx(ctx, tx, id, actor)
if err != nil {
    var conflict *publicops.ClaimConflictError
    if errors.As(err, &conflict) { return handleConflict(conflict) }
    if errors.Is(ctx.Err(), context.Canceled) { return ctx.Err() }
    return retryWithBackoff(func() error { _, err := ClaimIssueInTx(ctx, tx, id, actor); return err })
}

Prevention

When it happens

Trigger: Calling ClaimIssueInTx (via bd claim, ClaimReadyIssueInTx, ExecuteUpdate, ExecuteClaim) when the backing Dolt/SQL connection errors on the UPDATE: dropped connection, canceled context, constraint violation, or a backend engine error during the CAS write.

Common situations: Network blip between the CLI and the Dolt server mid-claim; the transaction's context deadline exceeded on a slow/loaded server; a driver/schema mismatch after upgrading bd while the DB is an older version.

Related errors


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