gastownhall/beads · error

failed to seed issue counter for prefix %q at %d: %w

Error message

failed to seed issue counter for prefix %q at %d: %w

What it means

SeedCounterFromExistingIssuesTx computed maxNum from existing issue IDs and failed to INSERT the seed row (prefix, maxNum) into issue_counter. The wrapped SQL error is the cause — typically a missing table, a duplicate-prefix constraint race, or a transaction error.

Source

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

		if err := rows.Scan(&id); err != nil {
			continue
		}
		suffix := strings.TrimPrefix(id, pfxDash)
		if strings.Contains(suffix, ".") {
			continue // skip child IDs
		}
		if n, err := strconv.Atoi(suffix); err == nil && n > maxNum {
			maxNum = n
		}
	}
	if err := rows.Err(); err != nil {
		return fmt.Errorf("failed to iterate issues for prefix %q: %w", prefix, err)
	}

	if maxNum > 0 {
		_, err = tx.ExecContext(ctx, "INSERT INTO issue_counter (prefix, last_id) VALUES (?, ?)", prefix, maxNum)
		if err != nil {
			return fmt.Errorf("failed to seed issue counter for prefix %q at %d: %w", prefix, maxNum, err)
		}
	}
	return nil
}

// GetAdaptiveIDLengthTx returns the appropriate hash length based on database size.
//
//nolint:gosec // G201: table is a hardcoded constant
func GetAdaptiveIDLengthTx(ctx context.Context, tx DBTX, table, prefix string) (int, error) {
	var count int
	err := tx.QueryRowContext(ctx, fmt.Sprintf(`
		SELECT COUNT(*)
		FROM %s
		WHERE id LIKE CONCAT(?, '-%%')
		  AND INSTR(SUBSTRING(id, LENGTH(?) + 2), '.') = 0
	`, table), prefix, prefix).Scan(&count)
	if err != nil {
		return 6, err

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run schema migrations so issue_counter exists.
  2. Use INSERT ... ON DUPLICATE KEY UPDATE / INSERT OR IGNORE semantics (or retry) to tolerate concurrent seeding races.
  3. Serialize writer access: avoid multiple concurrent bd processes on one DB, or take bd's write lock.
  4. Check the wrapped error for constraint vs. table-missing vs. permission causes and address specifically.

Example fix

// before (naive insert, races with concurrent seed)
_, err = tx.ExecContext(ctx, "INSERT INTO issue_counter (prefix, last_id) VALUES (?, ?)", prefix, maxNum)
// after (idempotent seeding at call site)
_, err = tx.ExecContext(ctx, `INSERT INTO issue_counter (prefix, last_id) VALUES (?, ?)
  ON DUPLICATE KEY UPDATE last_id = GREATEST(last_id, VALUES(last_id))`, prefix, maxNum)
Defensive patterns

Strategy: retry

Validate before calling

var n int
if err := tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM issue_counter").Scan(&n); err != nil {
    return fmt.Errorf("run migrations first: %w", err)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to seed issue counter") {
    // likely a concurrent-seed PK conflict; retry — seeding is idempotent
    time.Sleep(50 * time.Millisecond)
    return seedAndGenerate(ctx)
}

Prevention

When it happens

Trigger: First-time counter seeding for a prefix with existing issues: INSERT INTO issue_counter (prefix, last_id) VALUES (?, ?) fails — issue_counter table absent (unmigrated DB), a concurrent transaction inserted the same prefix row (primary-key conflict), or the tx was aborted.

Common situations: Two bd processes seeding the same prefix simultaneously; counter mode enabled on a DB that predates the issue_counter table; permission-restricted database user.

Related errors


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