gastownhall/beads · error

failed to generate unique ID after trying lengths %d-%d with

Error message

failed to generate unique ID after trying lengths %d-%d with 10 nonces each

What it means

This is an exhaustive-failure error from GenerateIssueIDInTable: after probing every length in the range (baseLength–maxLength) with 10 nonce variations each, every candidate ID already existed in the table. It is not a driver failure — the queries succeeded but every generated hash ID collided, which is astronomically unlikely unless something is systematically wrong.

Source

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

	}

	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)
	}
	return idMode == "counter", nil
}

// NextCounterIDTx atomically increments and returns the next sequential issue ID.
func NextCounterIDTx(ctx context.Context, tx DBTX, prefix string) (string, error) {
	res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
	if err != nil {
		return "", fmt.Errorf("failed to increment issue counter for prefix %q: %w", prefix, err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check for duplicate creation: the issue you're trying to create probably already exists — look it up instead of inserting again
  2. Ensure CreatedAt is a fresh timestamp per issue (check for frozen clocks or reused time values in import/retry code)
  3. Change the title/description or actor slightly, or regenerate with a different CreatedAt to alter the hash input
  4. Switch to counter mode (issue_id_mode=counter) which uses sequential IDs instead of hash probing

Example fix

// before
issue.CreatedAt = lastKnownTime // reused across retries
id, err := GenerateIssueIDInTable(ctx, tx, issue, actor)
// after
issue.CreatedAt = time.Now().UTC() // fresh timestamp per creation attempt
id, err := GenerateIssueIDInTable(ctx, tx, issue, actor)
Defensive patterns

Strategy: validation

Validate before calling

// check whether the issue already exists before attempting generation
existing, err := GetIssueInTx(ctx, tx, candidatePrefixID)
if err == nil && existing != nil && existing.Title == issue.Title {
	return existing, nil // idempotent re-create
}

Try / catch

id, err := GenerateIssueIDInTable(ctx, tx, issue, actor)
if err != nil {
	if strings.Contains(err.Error(), "failed to generate unique ID") {
		// exhaustion: check for duplicate create, or fall back to counter mode
		return detectAndReturnExistingIssue(ctx, tx, issue)
	}
	return err
}

Prevention

When it happens

Trigger: Calling GenerateIssueIDInTable when the same (prefix, title, description, actor, createdAt) tuple produces identical inputs and the DB contains the resulting IDs at every length/nonce combination — e.g. repeated bulk imports replaying the same issues, or clock skew/freezing causing identical CreatedAt values across many creates.

Common situations: Importing the same JSONL export twice; a retry loop recreating issues with identical title/description/actor/timestamp; tampered or stubbed clock in tests generating the same CreatedAt; extremely short configured ID lengths.

Related errors


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