gastownhall/beads · error

failed to check for ID collision: %w

Error message

failed to check for ID collision: %w

What it means

GenerateIssueIDInTable probes for a collision-free hash ID by running SELECT COUNT(*) against the issue table for each candidate. This error means that collision-check query itself failed (not that a collision was found), so ID generation must abort. The underlying driver error is preserved via %w.

Source

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

	// Default hash-based ID generation
	baseLength, err := GetAdaptiveIDLengthTx(ctx, tx, table, prefix)
	if err != nil {
		baseLength = 6
	}

	maxLength := 8
	if baseLength > maxLength {
		baseLength = maxLength
	}

	for length := baseLength; length <= maxLength; length++ {
		for nonce := 0; nonce < 10; nonce++ {
			candidate := idgen.GenerateHashID(prefix, issue.Title, issue.Description, actor, issue.CreatedAt, length, nonce)

			var count int
			err = tx.QueryRowContext(ctx, fmt.Sprintf(`SELECT COUNT(*) FROM %s WHERE id = ?`, table), candidate).Scan(&count)
			if err != nil {
				return "", fmt.Errorf("failed to check for ID collision: %w", err)
			}

			if count == 0 {
				return candidate, nil
			}
		}
	}

	return "", fmt.Errorf("failed to generate unique ID after trying lengths %d-%d with 10 nonces each", baseLength, maxLength)
}

// IsCounterModeTx checks whether issue_id_mode=counter is configured.
func IsCounterModeTx(ctx context.Context, tx DBTX) (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 {
		return false, fmt.Errorf("failed to read issue_id_mode config: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error to distinguish missing-table (schema issue) from lock/timeout issues
  2. Run schema migrations / bd doctor to confirm the issue table exists in this DB
  3. Retry the create once the conflicting transaction or lock is released
  4. If using counter mode, verify issue_id_mode config; counter mode skips hash probing entirely
Defensive patterns

Strategy: retry

Validate before calling

// confirm the target table exists before ID generation
var n int
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM information_schema.tables WHERE table_name = ?`, table).Scan(&n); err != nil || n == 0 {
	return fmt.Errorf("table %s missing: run migrations", table)
}

Try / catch

id, err := GenerateIssueIDInTable(ctx, tx, issue, actor)
if err != nil {
	if errors.Is(err, context.Canceled) || isLockTimeout(err) {
		// retry once with fresh transaction after backoff
	}
	return fmt.Errorf("generate issue id: %w", err)
}

Prevention

When it happens

Trigger: Calling GenerateIssueIDInTable (via assignCreateIssueIDInTx) when the issue table doesn't exist, the transaction was invalidated by an earlier error, the connection dropped mid-query, or a lock timeout occurs while counting rows for id = ?

Common situations: Running against a database where the issues table was dropped or renamed; schema migration incomplete after upgrade; embedded DB locked by another process; context canceled during a long create storm.

Related errors


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