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
- Inspect the wrapped error to distinguish missing-table (schema issue) from lock/timeout issues
- Run schema migrations / bd doctor to confirm the issue table exists in this DB
- Retry the create once the conflicting transaction or lock is released
- 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
- Keep the ID-generation and insert in the same short transaction to minimize lock windows
- Run migrations before app start so the issue table exists
- Avoid context timeouts shorter than expected query latency on large tables
- Monitor for other processes holding write locks on embedded databases
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
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- failed to check for ID collision: %w
- failed to close issue: %w
- failed to check issue existence: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bef36ed84977c317.
Report an issue: GitHub.