gastownhall/beads · error

journal: raise seq counter to high-water mark: %w

Error message

journal: raise seq counter to high-water mark: %w

What it means

healEventSeqCounter raises the singleton counter to GREATEST(next_seq, MAX(seq) in bd_events_journal); failure of that UPDATE is wrapped as 'journal: raise seq counter to high-water mark'. This keeps the seq allocator from re-issuing taken numbers; failure blocks journal writes and therefore the user's mutation.

Source

Thrown at internal/storage/issueops/journal.go:497

	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
// 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) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Update the Dolt driver/server to a version supporting aggregate subqueries in UPDATE
  2. Run migrations/doctor to ensure bd_events_journal exists
  3. Reduce concurrent writers or rely on the driver's row locking; retry the operation
  4. Restore the journal table if it was partially dropped/restored

Example fix

// before
-- partial restore left journal empty but seq ahead, ops failing oddly
DELETE FROM bd_events_journal;
-- after (full consistent restore, then let heal run)
RESTORE FROM 'backup.db';
-- then retry: healEventSeqCounter raises counter via GREATEST safely
Defensive patterns

Strategy: retry

Validate before calling

_, err := db.Exec("SELECT COALESCE(MAX(seq),0) FROM bd_events_journal")
if err != nil { return fmt.Errorf("bd_events_journal unreadable; migrate/restore first: %w", err) }

Try / catch

err := doWrite(ctx, tx)
if err != nil && strings.Contains(err.Error(), "raise seq counter") {
    if isTransientSQL(err) { return retryWithBackoff(doWrite) }
    return fmt.Errorf("persistent seq-heal failure, likely driver/schema: %w", err)
}

Prevention

When it happens

Trigger: The GREATEST/COALESCE subquery UPDATE fails: bd_events_journal missing, connection drop mid-transaction, Dolt refusing the aggregate subquery in UPDATE (version/feature gap), or lock contention.

Common situations: Old Dolt driver that cannot evaluate UPDATE with correlated aggregate subquery; journal table dropped or partially restored from backup; heavy contention on the counter row across many concurrent writers.

Related errors


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