gastownhall/beads · error

db: record event in %s: %w

Error message

db: record event in %s: %w

What it means

Wraps failures recording an event row into either the events or wisp_events table via issueops.RecordFullEventInTable. The %s names which table was targeted. Unlike some wisp paths, this does not tolerate missing tables, so any insert failure surfaces here.

Source

Thrown at internal/storage/domain/db/events.go:29

)

func NewEventsSQLRepository(runner Runner) domain.EventsSQLRepository {
	return &eventsSQLRepositoryImpl{runner: runner}
}

type eventsSQLRepositoryImpl struct {
	runner Runner
}

var _ domain.EventsSQLRepository = (*eventsSQLRepositoryImpl)(nil)

func (r *eventsSQLRepositoryImpl) Record(ctx context.Context, evt domain.Event, opts domain.RecordEventOpts) error {
	table := "events"
	if opts.UseWispsTable {
		table = "wisp_events"
	}
	if err := issueops.RecordFullEventInTable(ctx, r.runner, table, evt.IssueID, evt.Type, evt.Actor, evt.OldValue, evt.NewValue); err != nil {
		return fmt.Errorf("db: record event in %s: %w", table, err)
	}
	return nil
}

func (r *eventsSQLRepositoryImpl) DeleteAllForIDs(ctx context.Context, ids []string, opts domain.RecordEventOpts) (int, error) {
	if len(ids) == 0 {
		return 0, nil
	}
	table := "events"
	if opts.UseWispsTable {
		table = "wisp_events"
	}
	total := 0
	for start := 0; start < len(ids); start += deleteBatchSize {
		end := start + deleteBatchSize
		if end > len(ids) {
			end = len(ids)
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped driver error for FK violations or missing-table errors
  2. Run schema migrations so the events table exists (bd migrate / dolt schema ensure)
  3. Verify the target issue exists before recording the event
  4. Check disk/replication state if the DB is read-only

Example fix

// before
err := eventsRepo.Record(ctx, evt, domain.RecordEventOpts{})
// after
if _, err := issueRepo.Get(ctx, evt.IssueID, opts); err == nil {
    err = eventsRepo.Record(ctx, evt, domain.RecordEventOpts{})
}
Defensive patterns

Strategy: try-catch

Validate before calling

if evt.IssueID == "" { return fmt.Errorf("event requires IssueID") }
if _, err := issueRepo.Get(ctx, evt.IssueID, tableOpts); err != nil {
    return fmt.Errorf("target issue %s missing, skipping event", evt.IssueID)
}

Type guard

func isFKViolation(err error) bool {
    var mysqlErr *go_mysql.MySQLError
    return errors.As(err, &mysqlErr) && mysqlErr.Number == 1452
}

Try / catch

if err := eventsRepo.Record(ctx, evt, opts); err != nil {
    if isFKViolation(err) { log.Warnf("event for deleted issue %s dropped", evt.IssueID); return nil }
    return fmt.Errorf("record event: %w", err)
}

Prevention

When it happens

Trigger: Calling Record with opts.UseWispsTable=false and a missing/nonexistent events table, an invalid evt.IssueID violating a foreign key, oversized old/new values, connection failure, or a read-only replica.

Common situations: Recording an event for an issue that was deleted mid-transaction (FK violation); database in read-only mode; schema not yet migrated so the events table is absent.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/155a08cf9c679b41. Report an issue: GitHub.