gastownhall/beads · error
db: Claim %s: record event: %w
Error message
db: Claim %s: record event: %w
What it means
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.
Source
Thrown at internal/storage/domain/db/issue.go:534
// Grant the lease in the ephemeral leases table, mirroring
// issueops.ClaimIssueInTx. Wisps are never leased. This dual must stay in
// lockstep with the primary path (see the row_lock comment above).
if !opts.UseWispsTable {
if err := issueops.UpsertLeaseInTx(ctx, r.runner, id, actor, now, issueops.LeaseTTL(ctx)); err != nil {
return domain.ClaimRowResult{}, fmt.Errorf("db: Claim %s: %w", id, err)
}
}
oldData, _ := json.Marshal(oldIssue)
newData, _ := json.Marshal(map[string]any{"assignee": actor, "status": "in_progress"})
if err := r.events.Record(ctx, domain.Event{
IssueID: id,
Type: types.EventType("claimed"),
Actor: actor,
OldValue: string(oldData),
NewValue: string(newData),
}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
return domain.ClaimRowResult{}, fmt.Errorf("db: Claim %s: record event: %w", id, err)
}
// A claim changes assignee and status; the lost-CAS path returns above
// without writing and journals nothing.
if err := issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, id, actor); err != nil {
return domain.ClaimRowResult{}, err
}
return domain.ClaimRowResult{
Updated: true,
CurrentAssignee: actor,
CurrentStatus: types.StatusInProgress,
StartedAtWasZero: startedWasZero,
OldIssue: oldIssue,
}, nil
}
func (r *issueSQLRepositoryImpl) Get(ctx context.Context, id string, opts domain.IssueTableOpts) (*types.Issue, error) {
if id == "" {View on GitHub (pinned to 71377f2769)
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.
Example fix
// before
res, err := store.Claim(ctx, id, actor, opts) // event write fails silently retried
// after
if err != nil {
if errors.Is(err, context.Canceled) { return err } // do not blind-retry
res, err = store.Claim(ctx, id, actor, opts) // transactional: safe to retry
} Defensive patterns
Strategy: try-catch
Validate before calling
if err := ctx.Err(); err != nil { return err }
issue, err := store.Get(ctx, id, opts)
if err != nil { return err }
if len(issue.Description) > maxEventPayload { return fmt.Errorf("snapshot too large") } Try / catch
res, err := store.Claim(ctx, id, actor, opts)
switch {
case err == nil:
// claimed and journaled
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
return err // do not retry blindly
default:
res, err = store.Claim(ctx, id, actor, opts) // transactional rollback: safe retry
} Prevention
- 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.
When it happens
Trigger: Calling Claim where the events.Record call fails: events table write error, connection loss, oversized old/new payloads, or context cancellation mid-write.
Common situations: Events table corruption or schema drift; very large issue snapshots (oldData/newData JSON) hitting column limits; network failures to remote database during event write.
Related errors
- failed to record claim event: %w
- db: Claim %s: read old issue: %w
- db: Claim %s: resolve claim pools: %w
- db: Claim %s: resolve claimable statuses: %w
- db: Claim %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/387310c233328c8d.
Report an issue: GitHub.