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 (rowsAffected == 0), NextCounterIDTx calls SeedCounterFromExistingIssuesTx to derive last_id from existing issues. This error wraps any failure from that seeding step — e.g. the SELECT over existing issues failed, or the seed INSERT hit a constraint. The counter cannot be advanced until seeding succeeds.
Source
Thrown at internal/storage/issueops/helpers.go:243
}
return idMode == "counter", nil
}
// NextCounterIDTx atomically increments and returns the next sequential issue ID.
func NextCounterIDTx(ctx context.Context, tx DBTX, prefix string) (string, error) {
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 {
if seedErr := SeedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {
return "", fmt.Errorf("failed to seed issue counter for prefix %q: %w", prefix, seedErr)
}
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 {
_, 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)
}
}
}
var nextID intView on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped seed error: if it is a duplicate-key on issue_counter, another writer seeded concurrently — simply retry the operation.
- Run the schema/migration check to ensure both issues and issue_counter tables exist and are writable.
- Retry issue creation with backoff — the seed race is benign and resolves after the winner commits.
- Check DB write permissions and read-only mode if the seed INSERT is being refused.
- Avoid multiple concurrent first-writes per prefix in tests and tools by serializing initial issue creation.
Example fix
// before: for _, p := range prefixes { go createIssue(p) } // races seeding issue_counter. // after: single-flight per prefix — g.Go(func() error { return createIssueSerialized(p) }); g.Wait() Defensive patterns
Strategy: retry
Validate before calling
if _, err := tx.ExecContext(ctx, "INSERT IGNORE INTO issue_counter (prefix, last_id) VALUES (?, 0)", prefix); err != nil { return err } // ensure counter row exists before concurrent creation Type guard
func isSeedErr(err error) bool { return err != nil && strings.Contains(err.Error(), "failed to seed issue counter for prefix") } Try / catch
id, err := GenerateIssueIDInTable(ctx, tx, prefix, issue); if isSeedErr(err) { return retryWithBackoff(ctx, 3, 100*time.Millisecond, func() error { id, err = GenerateIssueIDInTable(ctx, tx, prefix, issue); return err }) } Prevention
- Pre-seed the issue_counter row for each prefix at bootstrap or migration time.
- Serialize the first issue creation per prefix (single-flight) to avoid seed races.
- Verify the issues table is readable before bulk inserts so the max-suffix scan works.
- Check the DB is not in read-only mode before initial writes.
When it happens
Trigger: First issue ever created with a new prefix while the issues or issue_counter tables are unreadable or unwritable; the seed INSERT conflicts with a concurrently inserted counter row (duplicate key); connection loss during the seed queries.
Common situations: Fresh database or freshly added prefix with concurrent writers racing to seed the counter; corrupted issues table making the max-suffix scan fail; schema migration that dropped issue_counter but left issues behind; read-only database refusing the seed INSERT.
Related errors
- failed to increment issue counter for prefix %q: %w
- db: ChildCounterSQLRepository.NextChildID: read counter for
- get next child ID: read counter: %w
- get next child ID: update counter: %w
- failed to increment issue counter for prefix %q: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e6aa4502ae3c8cd6.
Report an issue: GitHub.