{"record":{"id":"a04d6d4e745e0181","repo":"gastownhall/beads","slug":"db-claim-s-resolve-claim-pools-w","errorCode":null,"errorMessage":"db: Claim %s: resolve claim pools: %w","messagePattern":"db: Claim (.+?): resolve claim pools: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":425,"sourceCode":"\n\ttable := pickIssueTable(opts.UseWispsTable)\n\tnow := time.Now().UTC()\n\tstartedWasZero := oldIssue.StartedAt == nil\n\n\t// Rewrite row_lock exactly like the primary claim path (issueops.\n\t// ClaimIssueInTx). Without this, a claim made through the proxied-server\n\t// (uow) path leaves row_lock unchanged — open to the cell-merge bug the\n\t// row_lock invariant guards against (see issueops/lease.go). The lease\n\t// itself is granted into the ephemeral leases table below, after the CAS.\n\trowLockClause, rowLockArgs := issueops.RowLockClause()\n\n\t// Mirror the primary path's pool-aware predicate (bd-bguz6): aliases in\n\t// the claim.pools config are claimable by any actor. This dual must stay\n\t// in lockstep with issueops.ClaimIssueInTx — the lease comment above is\n\t// the scar from the last time it drifted.\n\tpools, err := issueops.ClaimPoolAliasesInTx(ctx, r.runner)\n\tif err != nil {\n\t\treturn domain.ClaimRowResult{}, fmt.Errorf(\"db: Claim %s: resolve claim pools: %w\", id, err)\n\t}\n\n\t// Claimability of the assignee slot, judged in Go against oldIssue rather\n\t// than as a spelling-sensitive SQL predicate (ga-v2k49, mirroring the\n\t// same-day fix to issueops.ClaimIssueInTx — this dual must stay in\n\t// lockstep, per the comment above): empty/unassigned, already this actor\n\t// — including a spelling difference across layers (ga-wzl83) — or a\n\t// claim-pool alias. issueops.ActorMatches is the exported form of the\n\t// primary path's package-local actorMatches, kept for exactly this dual.\n\tassigneeOK := oldIssue.Assignee == \"\" || issueops.ActorMatches(oldIssue.Assignee, actor) || slices.Contains(pools, oldIssue.Assignee)\n\n\t// Same lockstep for the source statuses (bd-pq7m2): claimable from \"open\"\n\t// plus custom active-category statuses, like the primary path — not a\n\t// hardcoded status = 'open'.\n\tclaimableStatuses, err := issueops.ClaimableSourceStatusesInTx(ctx, r.runner)\n\tif err != nil {\n\t\treturn domain.ClaimRowResult{}, fmt.Errorf(\"db: Claim %s: resolve claimable statuses: %w\", id, err)\n\t}","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L407-L443","documentation":"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.","triggerScenarios":"Calling Claim while the SQL reading claim pool aliases errors: connection failure, aborted transaction, missing or malformed claim.pools configuration storage, context cancellation.","commonSituations":"Corrupted or missing claim pool configuration rows after manual DB edits; connectivity blips; schema drift after upgrades that moved pool alias storage.","solutions":["Inspect the wrapped cause (%w) for the underlying SQL/driver error.","Verify the claim.pools configuration is present and readable in the database.","Re-run migrations if pool-alias storage schema is out of date.","Retry after connectivity is restored; the transaction rolled back cleanly."],"exampleFix":"// before\n// claim.pools config row deleted manually; Claim fails here\n// after\n// restore pool alias config, then retry claim\nbd update <pool-config-id> --notes \"re-added pool alias\"","handlingStrategy":"validation","validationCode":"// ensure claim pool config resolves before claiming\nif cfg.ClaimPools != nil {\n    for _, p := range cfg.ClaimPools {\n        if p.Name == \"\" { return fmt.Errorf(\"empty claim pool name\") }\n    }\n}","typeGuard":null,"tryCatchPattern":"_, err := store.Claim(ctx, id, actor, opts)\nif err != nil && strings.Contains(err.Error(), \"resolve claim pools\") {\n    return fmt.Errorf(\"claim pool config unreadable; check claim.pools: %w\", err)\n}","preventionTips":["Never hand-edit pool configuration rows in the database.","Validate claim.pools config after upgrades and migrations.","Run bd doctor to detect configuration storage problems.","Keep claim-path duals in lockstep when customizing pools."],"tags":["database","claim","configuration"],"backgroundTag":"claim-pool-config-invalid","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}