gastownhall/beads · error

journal: read seq counter: %w

Error message

journal: read seq counter: %w

What it means

After advancing (or healing via the duplicate-key reactive path in insertEventRow), nextEventSeq reads the allocated value with SELECT next_seq FROM bd_events_seq WHERE id = 0. This error wraps that read failing. Unlike journal_prune.go's version, ErrNoRows here is NOT tolerated — the seq row must exist because advance() either incremented it or created/healed it.

Source

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

		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 int64
	if err := tx.QueryRowContext(ctx, "SELECT next_seq FROM bd_events_seq WHERE id = 0").Scan(&seq); err != nil {
		return 0, fmt.Errorf("journal: read seq counter: %w", err)
	}
	return seq, nil
}

// compile-time assurance that *sql.Tx satisfies DBTX (the emit helpers accept
// both *sql.Tx and *sql.DB via DBTX).
var _ DBTX = (*sql.Tx)(nil)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the sentinel row exists: SELECT next_seq FROM bd_events_seq WHERE id = 0; re-insert it (INSERT INTO bd_events_seq (id, next_seq) VALUES (0, 1)) if missing.
  2. Re-run the schema migration to recreate bd_events_seq and its seed row.
  3. Retry the operation if the wrapped error is transient (connection/lock).
  4. Check for concurrent maintenance jobs that might truncate or rewrite bd_events_seq.

Example fix

// before
DELETE FROM bd_events_seq;            -- breaks next allocation
// after
UPDATE bd_events_seq SET next_seq = 1 WHERE id = 0;  -- reset without removing the sentinel row
Defensive patterns

Strategy: validation

Validate before calling

var seq int64
err := db.QueryRow("SELECT next_seq FROM bd_events_seq WHERE id = 0").Scan(&seq)
if errors.Is(err, sql.ErrNoRows) {
    _, _ = db.Exec("INSERT INTO bd_events_seq (id, next_seq) VALUES (0, 1)")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "journal: read seq counter") {
    return fmt.Errorf("journal seq row missing or unreadable; re-run migrations: %w", err)
}

Prevention

When it happens

Trigger: insertEventRow calling nextEventSeq when the SELECT on bd_events_seq returns a driver error (connection lost, table missing) — or returns ErrNoRows, which happens only if the row with id=0 vanished or the healing insert path silently failed to create it.

Common situations: Schema drift: bd_events_seq dropped or the id=0 row deleted manually; migration partially applied; transaction rolled back upstream so the healed row is invisible.

Related errors


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