gastownhall/beads · error

failed to check issue_counter for prefix %q: %w

Error message

failed to check issue_counter for prefix %q: %w

What it means

seedCounterFromExistingIssuesTx initializes the issue_counter table by reading last_id for a prefix; if the SELECT fails with an error other than sql.ErrNoRows, it is wrapped here. This is a query failure, not a missing row (missing row is expected and handled by seeding).

Source

Thrown at internal/storage/dolt/issues.go:801

	return wrapExecError("record event", issueops.RecordFullEventInTable(ctx, tx, "events", issueID, eventType, actor, oldValue, newValue))
}

// 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.
// This is called when counter mode is first enabled on a repo that already has issues,
// to prevent counter collisions with manually-created sequential IDs (GH#2002).
// It is idempotent: if a counter row already exists for this prefix, it does nothing.
func seedCounterFromExistingIssuesTx(ctx context.Context, tx *sql.Tx, prefix string) error {
	// Check whether a counter row already exists for this prefix.
	// If it does, we must not overwrite it (the counter may already be in use).
	var existing int
	err := tx.QueryRowContext(ctx, "SELECT last_id FROM issue_counter WHERE prefix = ?", prefix).Scan(&existing)
	if err == nil {
		// Row exists - counter is already initialized, nothing to do.
		return nil
	}
	if err != sql.ErrNoRows {
		return fmt.Errorf("failed to check issue_counter for prefix %q: %w", prefix, err)
	}

	// No counter row yet. Scan existing issues to find the highest numeric suffix.
	likePattern := prefix + "-%"
	rows, err := tx.QueryContext(ctx, "SELECT id FROM issues WHERE id LIKE ?", likePattern)
	if err != nil {
		return fmt.Errorf("failed to query existing issues for prefix %q: %w", prefix, err)
	}
	defer rows.Close()

	maxNum := 0
	prefixDash := prefix + "-"
	for rows.Next() {
		var id string
		if err := rows.Scan(&id); err != nil {
			return fmt.Errorf("failed to scan issue id: %w", err)
		}
		// Strip the prefix and attempt to parse the remainder as an integer.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped cause; check server connectivity and retry
  2. Verify the issue_counter table exists with columns (prefix, last_id); re-run schema migrations if not
  3. Run bd doctor to check database integrity
  4. If corruption is suspected, restore from backup or re-initialize the database
Defensive patterns

Strategy: retry

Validate before calling

// Verify database schema before ID generation
const ok = await tableExists("issue_counter"); // prefix TEXT, last_id BIGINT
if (!ok) throw new Error("run migrations: issue_counter missing");

Try / catch

try {
  await bd.create(title);
} catch (e) {
  if (String(e.message).includes("failed to check issue_counter")) {
    await runMigrations(); // then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Generating the next counter-based ID (nextCounterIDTx) when the issue_counter SELECT fails — connection errors, table missing/corrupt, transaction aborted.

Common situations: Fresh or migrated databases where issue_counter schema is missing; transient Dolt server failures mid-transaction; corrupted database files.

Related errors


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