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 never

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run schema migrations / bd doctor to create bd_events_seq
  2. Check INSERT privileges on bd_events_seq
  3. Verify DB connectivity and retry
  4. 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

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


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