gastownhall/beads · error
journal: seed seq counter: %w
Error message
journal: seed seq counter: %w
What it means
healEventSeqCounter seeds the singleton bd_events_seq row with INSERT IGNORE; if that exec fails the error is wrapped as 'journal: seed seq counter'. Seeding is part of seq-counter healing and of normal nextEventSeq allocation, so any journal write can surface this when the seq table is missing or the driver rejects the statement.
Source
Thrown at internal/storage/issueops/journal.go:490
if err != nil {
return err
}
if err := insert(seq); err != nil {
return fmt.Errorf("journal: record %s for %s after seq counter heal: %w", op, issueID, err)
}
}
return nil
}
// healEventSeqCounter seeds the counter row if it is missing and raises it to
// the journal's high-water mark, so the next allocation cannot collide. VALUES +
// GREATEST, not INSERT ... SELECT MAX(): in Dolt a literal+aggregate SELECT over
// an empty table yields zero rows, so an INSERT ... SELECT would seed nothing on
// a fresh journal. GREATEST also makes this safe to call on a counter that is
// already ahead — it never moves the counter backwards.
func healEventSeqCounter(ctx context.Context, tx DBTX) error {
if _, err := tx.ExecContext(ctx, "INSERT IGNORE INTO bd_events_seq (id, next_seq) VALUES (0, 0)"); err != nil {
return fmt.Errorf("journal: seed seq counter: %w", err)
}
if _, err := tx.ExecContext(ctx, `
UPDATE bd_events_seq
SET next_seq = GREATEST(next_seq, COALESCE((SELECT MAX(seq) FROM bd_events_journal), 0))
WHERE id = 0
`); err != nil {
return fmt.Errorf("journal: raise seq counter to high-water mark: %w", err)
}
return nil
}
// nextEventSeq allocates the next journal sequence number from the single-row
// bd_events_seq counter, INSIDE the caller's transaction. Incrementing the
// shared counter row is what serializes seq assignment: two transactions that
// both allocate a seq contend on the one row, so only one commit order survives.
// The value becomes the journal row's seq, yielding gapless, commit-ordered seqs
// (a rolled-back transaction rolls back its increment, burning no seq). The
// counter persists across restart and prune never touches it, so seq neverView on GitHub (pinned to 71377f2769)
Solutions
- Run schema migrations / bd doctor to create bd_events_seq
- Check INSERT privileges on bd_events_seq
- Verify DB connectivity and retry
- Confirm the Dolt driver version supports INSERT IGNORE
Example fix
// before
ops.RecordEventInTx(ctx, tx, op, id, nil, nil) // fails: bd_events_seq missing
// after
if err := migrate.Run(ctx, db); err != nil { return err } // creates bd_events_seq
ops.RecordEventInTx(ctx, tx, op, id, nil, nil) Defensive patterns
Strategy: validation
Validate before calling
_, err := db.Exec("SELECT 1 FROM bd_events_seq LIMIT 1")
if err != nil { return fmt.Errorf("bd_events_seq missing; run migrations: %w", err) } Try / catch
if err := doJournalWrite(ctx, tx); err != nil {
if strings.Contains(err.Error(), "seed seq counter") {
return fmt.Errorf("schema/permission issue on bd_events_seq: %w", err)
}
return err
} Prevention
- Run migrations whenever beads version changes
- Grant INSERT/UPDATE on bd_events_seq to the app user
- Verify driver supports INSERT IGNORE (MySQL/Dolt dialects)
When it happens
Trigger: bd_events_seq table missing (schema not migrated), INSERT IGNORE unsupported/failed by driver, connection error, or insufficient INSERT privileges on that table.
Common situations: Running a new beads version against an old database without migrations; read-only replicas receiving writes; Dolt version that mishandles INSERT IGNORE.
Related errors
- journal: read is_blocked for %s: %w
- journal: raise seq counter to high-water mark: %w
- failed to recompute is_blocked: %w
- failed to scan federation peer: %w
- failed to scan comment: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/02ba72d7be063233.
Report an issue: GitHub.