gastownhall/beads · error
failed to seed issue counter for prefix %q: %w
Error message
failed to seed issue counter for prefix %q: %w
What it means
When no issue_counter row exists for the prefix, the code seeds one from existing issue IDs via seedCounterFromExistingIssuesTx to avoid colliding with manually-created sequential IDs (GH#2002). This error wraps any failure of that seeding step — typically an error scanning existing issue numbers or an insert/update failure while writing the seeded counter.
Source
Thrown at internal/storage/dolt/issues.go:873
// 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)
}
rowsAffected, err = res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected after seeding for prefix %q: %w", prefix, err)
}
if rowsAffected == 0 {
// Seeding found no existing numeric IDs -- insert the initial row.
_, err = tx.ExecContext(ctx, "INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)", prefix)
if err != nil {
return "", fmt.Errorf("failed to insert initial issue counter for prefix %q: %w", prefix, err)
}
}
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped seed error; fix the underlying issues-table problem it reports (schema, corrupt rows, type mismatch).
- Manually create the counter row seeded to the max existing numeric ID: INSERT INTO issue_counter (prefix, last_id) VALUES ('<prefix>', <max_id>) — then retry.
- Verify the issues table contains parseable numeric suffixes for the prefix; clean malformed IDs.
- Run bd doctor to check database consistency before retrying.
Example fix
// before
if seedErr := seedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {
return "", fmt.Errorf("failed to seed issue counter for prefix %q: %w", prefix, seedErr)
}
// after (pre-seed the counter row outside of generation)
// bd> INSERT INTO issue_counter (prefix, last_id) VALUES ('GH', 42);
// then nextCounterIDTx skips the seeding path entirely
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix) Defensive patterns
Strategy: validation
Validate before calling
var lastID sql.NullInt64
err := db.QueryRow("SELECT MAX(CAST(SUBSTRING_INDEX(id,'-',-1) AS UNSIGNED)) FROM issues WHERE id LIKE ?", prefix+"-%").Scan(&lastID)
if err != nil {
return fmt.Errorf("issues table not seedable: %w", err)
}
// pre-create the counter row so the seeding path never runs
_, err = db.Exec("INSERT IGNORE INTO issue_counter (prefix, last_id) VALUES (?, ?)", prefix, lastID.Int64) Try / catch
id, err := store.CreateIssue(ctx, issue)
if err != nil && strings.Contains(err.Error(), "failed to seed issue counter") {
return fmt.Errorf("counter seeding failed; inspect issues table and pre-seed issue_counter: %w", err)
} Prevention
- Pre-seed issue_counter when migrating from hash-mode or imported databases
- Keep issue IDs well-formed (prefix-N) so seeding can parse them
- Run bd doctor on restored/converted databases before enabling counter mode
- Enable counter mode once, with a single process, then share the repository
When it happens
Trigger: First counter-mode ID generation in a database that never had a counter row for the prefix, where the seeding query over existing issues fails: issues table missing/unreadable, scan type mismatch on stored issue numbers, or the transaction aborted during seeding.
Common situations: Migrating an old hash-mode or JSON-exported database to counter mode; a database where issue IDs were created by another tool; partially restored .beads data where the issues table is inconsistent.
Related errors
- failed to query existing issues for prefix %q: %w
- failed to migrate credential keys: %w
- failed to scan peer for migration: %w
- failed to iterate peers for migration: %w
- failed to update encrypted password for peer %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8a1f18bd721c036a.
Report an issue: GitHub.