gastownhall/beads · error
failed to insert initial issue counter for prefix %q: %w
Error message
failed to insert initial issue counter for prefix %q: %w
What it means
When seeding found no existing numeric issue IDs, the code inserts the very first counter row `INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)`. This error wraps failure of that INSERT: primary-key conflict (another writer inserted the row in the meantime), schema/table missing, constraint violation, or transaction abort.
Source
Thrown at internal/storage/dolt/issues.go:888
// 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)
}
}
}
// Read back the value that was atomically set by the DB engine.
var nextID int
err = tx.QueryRowContext(ctx, "SELECT last_id FROM issue_counter WHERE prefix = ?", prefix).Scan(&nextID)
if err != nil {
return "", fmt.Errorf("failed to read issue counter after increment for prefix %q: %w", prefix, err)
}
return fmt.Sprintf("%s-%d", prefix, nextID), nil
}
// isCounterModeTx checks whether issue_id_mode=counter is configured.
func isCounterModeTx(ctx context.Context, tx *sql.Tx) (bool, error) {
var idMode string
err := tx.QueryRowContext(ctx, "SELECT value FROM config WHERE `key` = ?", "issue_id_mode").Scan(&idMode)
if err != nil && err != sql.ErrNoRows {View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped cause: if it is a duplicate-key error, the row now exists — just retry the operation (the counter is already initialized).
- Run the schema migration to ensure issue_counter exists, then retry.
- Serialize repository initialization: initialize once with a single bd process before running parallel commands.
- Check storage writability (disk space, read-only mount, server permissions) if the insert persists in failing.
Example fix
// before
_, 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)
}
// after (tolerate a concurrent initializer)
if _, err = tx.ExecContext(ctx, "INSERT IGNORE INTO issue_counter (prefix, last_id) VALUES (?, 1)", prefix); err != nil {
return "", fmt.Errorf("failed to insert initial issue counter for prefix %q: %w", prefix, err)
} 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("run schema migration before first counter-mode write")
} Type guard
func isDuplicateKeyErr(err error) bool {
return err != nil && (strings.Contains(err.Error(), "duplicate") || strings.Contains(err.Error(), "Duplicate entry"))
} Try / catch
id, err := store.CreateIssue(ctx, issue)
if err != nil && strings.Contains(err.Error(), "insert initial issue counter") {
if isDuplicateKeyErr(err) {
// another process initialized the counter; safe to retry
id, err = store.CreateIssue(ctx, issue)
}
} Prevention
- Initialize new repositories with a single bd process before parallelizing
- Ensure the issue_counter schema exists before enabling counter mode
- Check storage writability (disk space, permissions) on first use of a repo
- Use INSERT IGNORE/ON CONFLICT semantics when available in your beads version
When it happens
Trigger: First-ever counter-mode ID creation for a prefix in an empty database, where the initial INSERT fails — most commonly because a concurrent creator already inserted the same prefix row (duplicate-key error), or the issue_counter table doesn't exist.
Common situations: Two bd processes initializing a fresh repository simultaneously; a repository created by a newer beads version against an older schema without issue_counter; read-only or full-disk storage backends rejecting the write.
Related errors
- failed to seed issue_counter for prefix %q at %d: %w
- failed to seed issue counter for prefix %q at %d: %w
- failed to check issue_counter for prefix %q: %w
- failed to query existing issues for prefix %q: %w
- failed to scan issue id: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/293842b89b2e7ed6.
Report an issue: GitHub.