gastownhall/beads · error
failed to increment issue counter for prefix %q: %w
Error message
failed to increment issue counter for prefix %q: %w
What it means
This error wraps a failure of the atomic `UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?` statement executed inside nextCounterIDTx when generating a counter-mode issue ID. It indicates the SQL update itself returned a driver/database error (syntax, table missing, lock/transaction failure, connection drop), not that the row was absent. The %w wrap preserves the underlying Dolt/SQL error for diagnosis.
Source
Thrown at internal/storage/dolt/issues.go:861
return fmt.Errorf("failed to seed issue_counter for prefix %q at %d: %w", prefix, maxNum, err)
}
}
return nil
}
// nextCounterIDTx atomically increments and returns the next sequential issue ID
// for the given prefix within an existing transaction. Returns the full ID string
// (e.g., "bd-1"). Used by both generateIssueID and generateIssueIDInTable.
func nextCounterIDTx(ctx context.Context, tx *sql.Tx, prefix string) (string, error) {
// Increment atomically at the DB level to avoid duplicate IDs under
// concurrent transactions (GH#2002). "last_id = last_id + 1" is evaluated
// by the DB engine atomically within Dolt's MVCC.
// Attempt atomic increment of an existing counter row.
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
if err != nil {
return "", fmt.Errorf("failed to increment issue counter for prefix %q: %w", prefix, err)
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected for issue counter prefix %q: %w", prefix, err)
}
if rowsAffected == 0 {
// No counter row yet - seed from existing issues before proceeding to
// avoid collisions with manually-created sequential IDs (GH#2002).
if seedErr := seedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {
return "", fmt.Errorf("failed to seed issue counter for prefix %q: %w", prefix, seedErr)
}
// Retry the atomic increment after seeding.
res, err = tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
if err != nil {
return "", fmt.Errorf("failed to increment issue counter after seeding for prefix %q: %w", prefix, err)
}View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped cause (%w) for the specific SQL error; if 'table issue_counter not found', run the beads schema migration to create the issue_counter table.
- Verify the Dolt server/storage backend is healthy: restart the embedded/remote Dolt process and retry the operation.
- Confirm the transaction is still open and the context was not cancelled before the UPDATE (check upstream ctx deadlines).
- If corruption is suspected, run bd doctor / Dolt integrity checks on the repository database.
Example fix
// before
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
// after (ensure schema exists first)
if _, err := tx.ExecContext(ctx, "CREATE TABLE IF NOT EXISTS issue_counter (prefix VARCHAR(64) PRIMARY KEY, last_id BIGINT NOT NULL)"); err != nil {
return "", fmt.Errorf("ensure issue_counter table: %w", err)
}
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix) Defensive patterns
Strategy: retry
Validate before calling
var n int
if err := db.QueryRow("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'issue_counter'").Scan(&n); err != nil || n == 0 {
return fmt.Errorf("issue_counter table missing; run schema migration")
} Type guard
func isSQLStatementErr(err error) bool {
var sqlErr *sqle.Error
return errors.As(err, &sqlErr)
} Try / catch
id, err := store.CreateIssue(ctx, issue)
if err != nil && strings.Contains(err.Error(), "failed to increment issue counter") {
log.Printf("counter update failed: %v (underlying: %w)", err, errors.Unwrap(errors.Unwrap(err)))
// retry once after checking DB health
} Prevention
- Keep beads and its Dolt schema migrations up to date so issue_counter always exists
- Monitor disk space and Dolt server health for repositories you write to
- Avoid killing bd processes mid-issue-creation; let transactions commit
- Run bd doctor after storage-related failures before retrying
When it happens
Trigger: Calling generateIssueIDInTable (e.g. via issue creation with issue_id_mode=counter) when the UPDATE against the issue_counter table errors: the table does not exist in the current database version, the transaction was aborted/rolled back, the connection dropped mid-statement, or Dolt rejects the statement (lock timeout, MVCC conflict surfaced as error).
Common situations: Upgrading beads against an older .beads database whose schema lacks the issue_counter table; running with a stale or killed Dolt server process; disk full or corrupt Dolt working set causing statement failure; schema migration partially applied.
Related errors
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
- row iteration error: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bbd3be5436ce4d76.
Report an issue: GitHub.