gastownhall/beads · error
journal: advance seq counter: %w
Error message
journal: advance seq counter: %w
What it means
nextEventSeq advances the monolithic sequence row in bd_events_seq (UPDATE ... SET next_seq = next_seq + 1 WHERE id = 0) to allocate a journal sequence number for a new event row. This error wraps a failure of that UPDATE inside the caller's transaction and is the error carried up from insertEventRow. It means the journal write aborted before an event could even get a seq number.
Source
Thrown at internal/storage/issueops/journal.go:519
// 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 never
// resets. The seed row is created by migration 0064 / ignored 0022; the
// self-heal below re-creates it at the journal's high-water mark if it is ever
// missing, so a re-seed can never collide with an existing seq. A counter that
// is PRESENT but stale cannot be detected here without a per-emit MAX(seq)
// read, so insertEventRow heals that case reactively off the duplicate-key
// failure instead — see there.
func nextEventSeq(ctx context.Context, tx DBTX) (int64, error) {
advance := func() (int64, error) {
res, err := tx.ExecContext(ctx, "UPDATE bd_events_seq SET next_seq = next_seq + 1 WHERE id = 0")
if err != nil {
return 0, fmt.Errorf("journal: advance seq counter: %w", err)
}
return res.RowsAffected()
}
n, err := advance()
if err != nil {
return 0, err
}
if n == 0 {
// The counter row is missing entirely: seed it at the journal's
// high-water mark, so the re-seed can never collide with an existing seq.
if err := healEventSeqCounter(ctx, tx); err != nil {
return 0, err
}
if _, err := advance(); err != nil {
return 0, err
}
}
var seq int64View on GitHub (pinned to 71377f2769)
Solutions
- Run the schema migration to ensure bd_events_seq exists (verify with SELECT next_seq FROM bd_events_seq WHERE id = 0).
- Retry the operation: the wrapped error is often a transient deadlock/lock-timeout on the single seq row; re-run the whole transaction.
- Check database connectivity and server logs for connection drops during the transaction.
- Confirm the DB user has UPDATE privilege on bd_events_seq.
Example fix
// before
err := store.CreateIssue(ctx, issue) // fails: journal: advance seq counter: deadlock
// after
err := retry.OnDeadline(func() error { return store.CreateIssue(ctx, issue) }) // retry whole tx on transient lock errors; ensure migrations ran first Defensive patterns
Strategy: retry
Validate before calling
var n int64
if err := db.QueryRow("SELECT next_seq FROM bd_events_seq WHERE id = 0").Scan(&n); err != nil {
return fmt.Errorf("journal schema not migrated: %w", err)
} Try / catch
err := retry.Do(func() error {
_, err := store.CreateIssue(ctx, issue)
if err != nil && strings.Contains(err.Error(), "journal: advance seq counter") && isTransient(err) {
return retry.RetryableError(err)
}
return err
}) Prevention
- Always run migrations before opening the store for writes.
- Keep write transactions short to avoid deadlocks on the single seq row.
- Wrap batch writes with bounded retry on transient SQL errors.
- Monitor for lock-wait-timeout errors in DB logs.
When it happens
Trigger: Calling any event-emitting storage operation (e.g. via insertEventRow) when the UPDATE on bd_events_seq fails: the transaction is already broken (deadlock, lock wait timeout), the table is missing (schema not migrated, table dropped), the connection was lost mid-transaction, or a driver-level SQL syntax/permission failure occurs.
Common situations: Concurrent writers deadlocking on the single seq row; an old database opened by a newer binary before migration added bd_events_seq; connection killed by network blip or server timeout during a long transaction; insufficient privileges for the app DB user.
Related errors
- journal: read seq counter: %w
- journal: read seq counter: %w
- journal: read since %d: %w
- set repo mtime: %w
- clear repo mtime: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/25828340964c9d46.
Report an issue: GitHub.