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

  1. Inspect the wrapped seed error; fix the underlying issues-table problem it reports (schema, corrupt rows, type mismatch).
  2. 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.
  3. Verify the issues table contains parseable numeric suffixes for the prefix; clean malformed IDs.
  4. 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

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


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