{"record":{"id":"761c8ec12bc25680","repo":"gastownhall/beads","slug":"get-claimed-issue-w","errorCode":null,"errorMessage":"get claimed issue: %w","messagePattern":"get claimed issue: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/claim.go","lineNumber":296,"sourceCode":"\t// here.\n\tclaimFilter.Limit = 0\n\tclaimFilter.MaxRows = 0\n\tclaimFilter.MaxRowsSource = \"\"\n\n\treadyIssues, err := GetReadyWorkInTx(ctx, tx, claimFilter)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, issue := range readyIssues {\n\t\tif _, err := ClaimIssueInTx(ctx, tx, issue.ID, actor); err != nil {\n\t\t\tif errors.Is(err, storage.ErrAlreadyClaimed) || errors.Is(err, storage.ErrNotClaimable) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t\tclaimed, err := GetIssueInTx(ctx, tx, issue.ID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get claimed issue: %w\", err)\n\t\t}\n\t\treturn claimed, nil\n\t}\n\treturn nil, nil\n}\n\n// ClaimPoolAliasesInTx returns the pool pseudo-assignee aliases from the\n// claim.pools config key (comma-separated, whitespace-trimmed). An issue\n// assigned to one of these aliases is claimable by ANY actor through the\n// normal claim CAS — the pattern where a dispatcher pre-assigns work to a\n// group alias (e.g. \"fable-crew\") and members take items from the pool.\n// Issues assigned to a real actor are unaffected. Missing/empty config (the\n// default) disables pool-aware claiming entirely.\nfunc ClaimPoolAliasesInTx(ctx context.Context, tx DBTX) ([]string, error) {\n\traw, err := GetConfigInTx(ctx, tx, \"claim.pools\")\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/claim.go#L278-L314","documentation":"ClaimReadyIssueInTx claims the first ready issue, then re-reads the claimed row with GetIssueInTx to return the authoritative post-claim state. This error wraps a failure of that follow-up read, meaning the claim likely succeeded but the fresh row could not be fetched inside the same transaction.","triggerScenarios":"After ClaimIssueInTx succeeds inside ClaimReadyIssueInTx (via ExecuteClaimNext), GetIssueInTx errors — connection drop, context cancellation, or the row vanished mid-transaction.","commonSituations":"Flaky connection between the CAS UPDATE and the re-read; a canceled CLI context racing the read; a concurrent transaction that deleted the just-claimed issue.","solutions":["Retry bd claim — a same-actor re-claim is idempotent, so retrying is safe","Check DB connectivity if the wrapped error is a connection failure","Refresh the ready queue and claim a different issue if this one raced away"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"db unavailable: %w\", err) }","typeGuard":null,"tryCatchPattern":"claimed, err := ClaimReadyIssueInTx(ctx, tx, filter, actor)\nif err != nil && !errors.Is(err, storage.ErrValidation) {\n    return retryClaimNext(ctx, tx, filter, actor) // re-claim is safe/idempotent\n}","preventionTips":["Keep transactions short and contexts alive through the whole claim","Use stable connections for claim-next loops","Retry claim-next: any issue already claimed by this actor returns idempotently"],"tags":["database","claim","concurrency","storage"],"backgroundTag":"claim-race-condition","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}