{"record":{"id":"387310c233328c8d","repo":"gastownhall/beads","slug":"db-claim-s-record-event-w","errorCode":null,"errorMessage":"db: Claim %s: record event: %w","messagePattern":"db: Claim (.+?): record event: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":534,"sourceCode":"\t// Grant the lease in the ephemeral leases table, mirroring\n\t// issueops.ClaimIssueInTx. Wisps are never leased. This dual must stay in\n\t// lockstep with the primary path (see the row_lock comment above).\n\tif !opts.UseWispsTable {\n\t\tif err := issueops.UpsertLeaseInTx(ctx, r.runner, id, actor, now, issueops.LeaseTTL(ctx)); err != nil {\n\t\t\treturn domain.ClaimRowResult{}, fmt.Errorf(\"db: Claim %s: %w\", id, err)\n\t\t}\n\t}\n\n\toldData, _ := json.Marshal(oldIssue)\n\tnewData, _ := json.Marshal(map[string]any{\"assignee\": actor, \"status\": \"in_progress\"})\n\tif err := r.events.Record(ctx, domain.Event{\n\t\tIssueID:  id,\n\t\tType:     types.EventType(\"claimed\"),\n\t\tActor:    actor,\n\t\tOldValue: string(oldData),\n\t\tNewValue: string(newData),\n\t}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {\n\t\treturn domain.ClaimRowResult{}, fmt.Errorf(\"db: Claim %s: record event: %w\", id, err)\n\t}\n\t// A claim changes assignee and status; the lost-CAS path returns above\n\t// without writing and journals nothing.\n\tif err := issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, id, actor); err != nil {\n\t\treturn domain.ClaimRowResult{}, err\n\t}\n\n\treturn domain.ClaimRowResult{\n\t\tUpdated:          true,\n\t\tCurrentAssignee:  actor,\n\t\tCurrentStatus:    types.StatusInProgress,\n\t\tStartedAtWasZero: startedWasZero,\n\t\tOldIssue:         oldIssue,\n\t}, nil\n}\n\nfunc (r *issueSQLRepositoryImpl) Get(ctx context.Context, id string, opts domain.IssueTableOpts) (*types.Issue, error) {\n\tif id == \"\" {","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L516-L552","documentation":"This error wraps a failure to record the 'claimed' event (with old/new JSON snapshots) via r.events.Record after a successful claim. The event journal is part of the claim's transactional semantics; if recording fails, the whole Claim is rolled back with this wrapped error.","triggerScenarios":"Calling Claim where the events.Record call fails: events table write error, connection loss, oversized old/new payloads, or context cancellation mid-write.","commonSituations":"Events table corruption or schema drift; very large issue snapshots (oldData/newData JSON) hitting column limits; network failures to remote database during event write.","solutions":["Inspect the wrapped cause (%w) for the underlying SQL/driver error.","Verify the events table schema and integrity; re-run migrations if drifted.","Check payload size — unusually large issue snapshots can exceed column limits.","Retry the claim; the transaction rolled back so nothing was partially written."],"exampleFix":"// before\nres, err := store.Claim(ctx, id, actor, opts) // event write fails silently retried\n// after\nif err != nil {\n    if errors.Is(err, context.Canceled) { return err } // do not blind-retry\n    res, err = store.Claim(ctx, id, actor, opts) // transactional: safe to retry\n}","handlingStrategy":"try-catch","validationCode":"if err := ctx.Err(); err != nil { return err }\nissue, err := store.Get(ctx, id, opts)\nif err != nil { return err }\nif len(issue.Description) > maxEventPayload { return fmt.Errorf(\"snapshot too large\") }","typeGuard":null,"tryCatchPattern":"res, err := store.Claim(ctx, id, actor, opts)\nswitch {\ncase err == nil:\n    // claimed and journaled\ncase errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):\n    return err // do not retry blindly\ndefault:\n    res, err = store.Claim(ctx, id, actor, opts) // transactional rollback: safe retry\n}","preventionTips":["Keep event payload sizes bounded (trim huge issue descriptions).","Maintain events-table schema via migrations.","Avoid canceling claim contexts mid-operation.","Set claim timeouts generously so event writes complete."],"tags":["database","claim","event-journal"],"backgroundTag":"event-record-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}