{"record":{"id":"784318a0a896dab1","repo":"gastownhall/beads","slug":"failed-to-get-issue-for-claim-w","errorCode":null,"errorMessage":"failed to get issue for claim: %w","messagePattern":"failed to get issue for claim: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/claim.go","lineNumber":49,"sourceCode":"// success (supports agent retry workflows).\n// Routes to the correct table (issues/wisps) automatically.\n// The caller is responsible for Dolt versioning (DOLT_ADD/COMMIT) if needed.\n//\n//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)\nfunc ClaimIssueInTx(ctx context.Context, tx DBTX, id string, actor string) (*ClaimResult, error) {\n\t// The CAS below writes assignee = actor. actor is user-settable (--actor /\n\t// BEADS_ACTOR), so bound it against the VARCHAR(255) assignee column up front\n\t// and return a typed ErrFieldTooLong rather than a raw backend error.\n\tif err := types.CheckFieldLen(\"actor\", actor); err != nil {\n\t\treturn nil, err\n\t}\n\tisWisp := IsActiveWispInTx(ctx, tx, id)\n\tissueTable, _, eventTable, _ := WispTableRouting(isWisp)\n\n\t// Read old issue inside the transaction for event recording.\n\toldIssue, err := GetIssueInTx(ctx, tx, id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get issue for claim: %w\", err)\n\t}\n\n\tnow := time.Now().UTC()\n\n\t// Rewrite row_lock with the claim (see lease.go): a concurrent reclaim or\n\t// close on the same row is forced to conflict rather than silently\n\t// cell-merge. The lease itself is granted separately below, in the\n\t// ephemeral leases table — claims commit (status/assignee are\n\t// history-worthy) but lease grants and heartbeats do not (bd-lrgn1).\n\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)","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/claim.go#L31-L67","documentation":"ClaimIssueInTx reads the pre-image of the issue inside the transaction (via GetIssueInTx) before applying the claim CAS; failure is wrapped as \"failed to get issue for claim: %w\". The pre-image is required both for event recording and for assignee-claimability checks, so a claim cannot proceed without it.","triggerScenarios":"Calling ClaimIssueInTx / ExecuteClaim / ExecuteUpdate when: the issue ID does not exist (ErrNoRows from GetIssueInTx), the row routing (wisp vs issue table) mismatched so the lookup hit the wrong table, or the SELECT failed from connection/context errors.","commonSituations":"Claiming an issue that was already deleted in another session; stale ID from a jsonl export; claiming a wisp ID through the non-wisp path after a compaction moved it; typos in the issue ID.","solutions":["Verify the issue exists first: bd show <id>; if gone, the claim target is stale.","Check the wrapped error: 'no rows' means wrong or deleted ID; connection errors mean retry the transaction.","Re-sync (bd sync) if the ID comes from another machine's export and may have been compacted or deleted.","Ensure you pass the full ID (prefix + number) exactly as stored."],"exampleFix":"// before\niss, err := store.ClaimIssue(ctx, id, actor)\nif err != nil { return err }\n// after\niss, err := store.ClaimIssue(ctx, id, actor)\nif err != nil {\n    if strings.Contains(err.Error(), \"no rows\") {\n        return fmt.Errorf(\"issue %s not found; run bd sync\", id)\n    }\n    return err\n}","handlingStrategy":"validation","validationCode":"iss, err := store.GetIssue(ctx, id)\nif err != nil {\n    return fmt.Errorf(\"cannot claim %s: issue not found; run bd sync\", id)\n}","typeGuard":"func issueExists(iss *types.Issue, err error) bool {\n    return err == nil && iss != nil && iss.ID != \"\"\n}","tryCatchPattern":"iss, err := store.ClaimIssue(ctx, id, actor)\nif err != nil {\n    if strings.Contains(err.Error(), \"no rows\") || strings.Contains(err.Error(), \"not found\") {\n        return fmt.Errorf(\"issue %s missing locally; run bd sync\", id)\n    }\n    return err\n}","preventionTips":["Run bd sync before scripted claims so local IDs are current.","Validate issue IDs (bd show) before claim loops.","Handle deletions: another agent may have closed/deleted the issue.","Use bd ready to pick claimable IDs instead of caching stale ID lists."],"tags":["database","claim","issue-not-found"],"backgroundTag":"issue-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}