gastownhall/beads · error

db: Claim %s: resolve claim pools: %w

Error message

db: Claim %s: resolve claim pools: %w

What it means

This error wraps a failure of ClaimPoolAliasesInTx, which resolves claim pool aliases (pools claimable by any actor) inside the Claim transaction. This dual path must mirror the primary ClaimIssueInTx predicate; if the pool-alias lookup fails, Claim aborts with this wrapped error.

Source

Thrown at internal/storage/domain/db/issue.go:425

	table := pickIssueTable(opts.UseWispsTable)
	now := time.Now().UTC()
	startedWasZero := oldIssue.StartedAt == nil

	// Rewrite row_lock exactly like the primary claim path (issueops.
	// ClaimIssueInTx). Without this, a claim made through the proxied-server
	// (uow) path leaves row_lock unchanged — open to the cell-merge bug the
	// row_lock invariant guards against (see issueops/lease.go). The lease
	// itself is granted into the ephemeral leases table below, after the CAS.
	rowLockClause, rowLockArgs := issueops.RowLockClause()

	// Mirror the primary path's pool-aware predicate (bd-bguz6): aliases in
	// the claim.pools config are claimable by any actor. This dual must stay
	// in lockstep with issueops.ClaimIssueInTx — the lease comment above is
	// the scar from the last time it drifted.
	pools, err := issueops.ClaimPoolAliasesInTx(ctx, r.runner)
	if err != nil {
		return domain.ClaimRowResult{}, fmt.Errorf("db: Claim %s: resolve claim pools: %w", id, err)
	}

	// Claimability of the assignee slot, judged in Go against oldIssue rather
	// than as a spelling-sensitive SQL predicate (ga-v2k49, mirroring the
	// same-day fix to issueops.ClaimIssueInTx — this dual must stay in
	// lockstep, per the comment above): empty/unassigned, already this actor
	// — including a spelling difference across layers (ga-wzl83) — or a
	// claim-pool alias. issueops.ActorMatches is the exported form of the
	// primary path's package-local actorMatches, kept for exactly this dual.
	assigneeOK := oldIssue.Assignee == "" || issueops.ActorMatches(oldIssue.Assignee, actor) || slices.Contains(pools, oldIssue.Assignee)

	// Same lockstep for the source statuses (bd-pq7m2): claimable from "open"
	// plus custom active-category statuses, like the primary path — not a
	// hardcoded status = 'open'.
	claimableStatuses, err := issueops.ClaimableSourceStatusesInTx(ctx, r.runner)
	if err != nil {
		return domain.ClaimRowResult{}, fmt.Errorf("db: Claim %s: resolve claimable statuses: %w", id, err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w) for the underlying SQL/driver error.
  2. Verify the claim.pools configuration is present and readable in the database.
  3. Re-run migrations if pool-alias storage schema is out of date.
  4. Retry after connectivity is restored; the transaction rolled back cleanly.

Example fix

// before
// claim.pools config row deleted manually; Claim fails here
// after
// restore pool alias config, then retry claim
bd update <pool-config-id> --notes "re-added pool alias"
Defensive patterns

Strategy: validation

Validate before calling

// ensure claim pool config resolves before claiming
if cfg.ClaimPools != nil {
    for _, p := range cfg.ClaimPools {
        if p.Name == "" { return fmt.Errorf("empty claim pool name") }
    }
}

Try / catch

_, err := store.Claim(ctx, id, actor, opts)
if err != nil && strings.Contains(err.Error(), "resolve claim pools") {
    return fmt.Errorf("claim pool config unreadable; check claim.pools: %w", err)
}

Prevention

When it happens

Trigger: Calling Claim while the SQL reading claim pool aliases errors: connection failure, aborted transaction, missing or malformed claim.pools configuration storage, context cancellation.

Common situations: Corrupted or missing claim pool configuration rows after manual DB edits; connectivity blips; schema drift after upgrades that moved pool alias storage.

Related errors


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