{"record":{"id":"cf7036d312d1235d","repo":"gastownhall/beads","slug":"failed-to-resolve-claim-pools-w","errorCode":null,"errorMessage":"failed to resolve claim pools: %w","messagePattern":"failed to resolve claim pools: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/claim.go","lineNumber":77,"sourceCode":"\trowLockClause, rowLockArgs := RowLockClause()\n\n\t// An issue is claimable from \"open\" plus any configured custom status whose\n\t// category is \"active\" (e.g. a draft->ready->in_progress lifecycle where\n\t// \"ready\" should be claimable). WIP/done/frozen customs are excluded so the\n\t// anti-steal protection from GH-3570 is preserved.\n\tclaimableStatuses, err := ClaimableSourceStatusesInTx(ctx, tx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to resolve claimable statuses: %w\", err)\n\t}\n\tstatusPlaceholders, statusArgs := buildSQLInClause(claimableStatuses)\n\n\t// Pool-aware claim (bd-bguz6): a dispatcher may pre-assign issues to a\n\t// pool pseudo-assignee (e.g. \"fable-crew\"). Aliases listed in the\n\t// claim.pools config are claimable by any actor through the same CAS;\n\t// issues assigned to a real actor keep their anti-steal protection.\n\tpools, err := ClaimPoolAliasesInTx(ctx, tx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to resolve claim pools: %w\", err)\n\t}\n\n\t// Claimability of the assignee slot, judged in Go against oldIssue (the\n\t// pre-image read above, inside this same transaction) rather than as a\n\t// spelling-sensitive SQL predicate (ga-v2k49, same shape as ga-5ksp5's\n\t// unclaim.go fix): empty/unassigned, already this actor — including an\n\t// idempotent re-claim spelled under a different layer's separator\n\t// convention (ga-wzl83) — or assigned to a claim-pool alias. That last\n\t// membership test is deliberately exact-string, unlike actorMatches right\n\t// beside it: a pool alias (e.g. \"fable-crew\") is a literal claim.pools\n\t// config value, not a Gas Town identity that gets respelled per layer, so\n\t// there is no cross-spelling variant to reconcile — canonicalizing it\n\t// could only blur two administratively-distinct pool names into one.\n\tassigneeOK := oldIssue.Assignee == \"\" || actorMatches(oldIssue.Assignee, actor) || slices.Contains(pools, oldIssue.Assignee)\n\n\t// Conditional UPDATE: only attempted while the issue was still claimable\n\t// as of oldIssue, and even then CASed on row_lock rather than re-checking\n\t// assignee/status in SQL — row_lock is rewritten by every path that","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/claim.go#L59-L95","documentation":"ClaimIssueInTx resolves claim pool aliases (pool pseudo-assignees like 'fable-crew' that any actor may claim through) via ClaimPoolAliasesInTx; failure is wrapped as \"failed to resolve claim pools: %w\". Pools enable dispatcher pre-assignment; without resolving them the pool-aware CAS cannot be built. The error means reading the claim.pools config from the DB failed.","triggerScenarios":"Claiming when: the pool configuration storage is missing or unreadable, the transaction is aborted by an earlier error, the context is cancelled, or the connection fails during the config SELECT.","commonSituations":"Older databases without the claim-pool config storage; malformed claim.pools configuration after manual editing; dispatcher loops hitting transient DB errors under load.","solutions":["Migrate/upgrade the database so the pool-alias config storage exists.","Retry the claim transaction if the wrapped error is transient (connection/lock).","Validate the claim.pools config (remove malformed entries) and re-check with bd doctor.","Check the wrapped error text to distinguish missing-table (migrate) vs timeout (contention)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if err := validatePoolsConfig(cfg.Claim.Pools); err != nil {\n    return fmt.Errorf(\"invalid claim.pools config: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    tx.Rollback()\n    return retry(fmt.Errorf(\"claim pool resolution failed: %w\", err))\n}","preventionTips":["Keep bd upgraded so pool config storage is migrated.","Validate claim.pools entries (valid assignee names) before dispatching.","Use backoff-retries for pool resolution errors in high-throughput dispatchers.","Avoid manual edits to pool configuration storage."],"tags":["database","claim","pools","configuration"],"backgroundTag":"config-resolution-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}