gastownhall/beads · error

failed to check existing counter for prefix %q: %w

Error message

failed to check existing counter for prefix %q: %w

What it means

SeedCounterFromExistingIssuesTx probes the issue_counter table to see if the prefix is already seeded; the probe failed with an error other than 'no rows'. This is a database-level failure (table missing, connection error, query error), not a 'not seeded yet' condition.

Source

Thrown at internal/storage/issueops/helpers.go:278

	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
}

// SeedCounterFromExistingIssuesTx scans existing issues to find the highest numeric suffix
// for the given prefix, then seeds the issue_counter table if no row exists yet.
func SeedCounterFromExistingIssuesTx(ctx context.Context, tx DBTX, prefix string) error {
	var existing int
	err := tx.QueryRowContext(ctx, "SELECT last_id FROM issue_counter WHERE prefix = ?", prefix).Scan(&existing)
	if err == nil {
		return nil // already seeded
	}
	if err != sql.ErrNoRows {
		return fmt.Errorf("failed to check existing counter for prefix %q: %w", prefix, err)
	}

	// Find max numeric suffix among existing issues
	rows, err := tx.QueryContext(ctx, `SELECT id FROM issues WHERE id LIKE CONCAT(?, '-%')`, prefix)
	if err != nil {
		return fmt.Errorf("failed to scan existing issues for prefix %q: %w", prefix, err)
	}
	defer rows.Close()

	maxNum := 0
	pfxDash := prefix + "-"
	for rows.Next() {
		var id string
		if err := rows.Scan(&id); err != nil {
			continue
		}
		suffix := strings.TrimPrefix(id, pfxDash)
		if strings.Contains(suffix, ".") {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Apply schema migrations so issue_counter exists, then retry (the seeding will then succeed or find the row).
  2. Inspect the wrapped error: 'no such table' → migrate; permission denied → grant SELECT on issue_counter.
  3. Verify DB file integrity / re-clone via 'bd bootstrap' if the .beads database is corrupted.
  4. Check driver connection health before the operation if errors are transient.

Example fix

// before: old DB without issue_counter table
// after
if err := storage.Migrate(ctx, db); err != nil { return err }
id, err := storage.GenerateIssueIDInTable(ctx, tx, "issues", prefix, issue)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := tx.QueryContext(ctx, "SELECT 1 FROM issue_counter LIMIT 1"); err != nil {
    return fmt.Errorf("run migrations: issue_counter table unavailable: %w", err)
}

Try / catch

id, err := storage.GenerateIssueIDInTable(ctx, tx, "issues", prefix, issue)
if err != nil && strings.Contains(err.Error(), "failed to check existing counter") {
    if mErr := storage.Migrate(ctx, db); mErr != nil { return mErr }
    id, err = storage.GenerateIssueIDInTable(ctx, tx, "issues", prefix, issue)
}

Prevention

When it happens

Trigger: Called from NextCounterIDTx when an UPDATE on issue_counter matched 0 rows: the initial SELECT last_id ... Scan returns a non-ErrNoRows error — most commonly 'no such table: issue_counter' on an unmigrated Dolt database.

Common situations: Database created before counter-mode migration; corrupted or partially restored .beads Dolt database; connecting to a DB with insufficient privileges to read issue_counter.

Related errors


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