{"record":{"id":"90817526b925405f","repo":"gastownhall/beads","slug":"failed-to-generate-unique-id-after-trying-lengths-908175","errorCode":null,"errorMessage":"failed to generate unique ID after trying lengths %d-%d with 10 nonces each","messagePattern":"failed to generate unique ID after trying lengths (.+?)-(.+?) with 10 nonces each","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/helpers.go","lineNumber":216,"sourceCode":"\t}\n\n\tfor length := baseLength; length <= maxLength; length++ {\n\t\tfor nonce := 0; nonce < 10; nonce++ {\n\t\t\tcandidate := idgen.GenerateHashID(prefix, issue.Title, issue.Description, actor, issue.CreatedAt, length, nonce)\n\n\t\t\tvar count int\n\t\t\terr = tx.QueryRowContext(ctx, fmt.Sprintf(`SELECT COUNT(*) FROM %s WHERE id = ?`, table), candidate).Scan(&count)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to check for ID collision: %w\", err)\n\t\t\t}\n\n\t\t\tif count == 0 {\n\t\t\t\treturn candidate, nil\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"failed to generate unique ID after trying lengths %d-%d with 10 nonces each\", baseLength, maxLength)\n}\n\n// IsCounterModeTx checks whether issue_id_mode=counter is configured.\nfunc IsCounterModeTx(ctx context.Context, tx DBTX) (bool, error) {\n\tvar idMode string\n\terr := tx.QueryRowContext(ctx, \"SELECT value FROM config WHERE `key` = ?\", \"issue_id_mode\").Scan(&idMode)\n\tif err != nil && err != sql.ErrNoRows {\n\t\treturn false, fmt.Errorf(\"failed to read issue_id_mode config: %w\", err)\n\t}\n\treturn idMode == \"counter\", nil\n}\n\n// NextCounterIDTx atomically increments and returns the next sequential issue ID.\nfunc NextCounterIDTx(ctx context.Context, tx DBTX, prefix string) (string, error) {\n\tres, err := tx.ExecContext(ctx, \"UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?\", prefix)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to increment issue counter for prefix %q: %w\", prefix, err)\n\t}","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/helpers.go#L198-L234","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check for duplicate creation: the issue you're trying to create probably already exists — look it up instead of inserting again","Ensure CreatedAt is a fresh timestamp per issue (check for frozen clocks or reused time values in import/retry code)","Change the title/description or actor slightly, or regenerate with a different CreatedAt to alter the hash input","Switch to counter mode (issue_id_mode=counter) which uses sequential IDs instead of hash probing"],"exampleFix":"// before\nissue.CreatedAt = lastKnownTime // reused across retries\nid, err := GenerateIssueIDInTable(ctx, tx, issue, actor)\n// after\nissue.CreatedAt = time.Now().UTC() // fresh timestamp per creation attempt\nid, err := GenerateIssueIDInTable(ctx, tx, issue, actor)","handlingStrategy":"validation","validationCode":"// check whether the issue already exists before attempting generation\nexisting, err := GetIssueInTx(ctx, tx, candidatePrefixID)\nif err == nil && existing != nil && existing.Title == issue.Title {\n\treturn existing, nil // idempotent re-create\n}","typeGuard":null,"tryCatchPattern":"id, err := GenerateIssueIDInTable(ctx, tx, issue, actor)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to generate unique ID\") {\n\t\t// exhaustion: check for duplicate create, or fall back to counter mode\n\t\treturn detectAndReturnExistingIssue(ctx, tx, issue)\n\t}\n\treturn err\n}","preventionTips":["Never reuse a stale CreatedAt value across create retries — always use a fresh time.Now()","Deduplicate imports: look up issues by content before creating","Use counter mode (issue_id_mode=counter) for high-volume or deterministic-clock environments","Keep the ID length range reasonably wide (default baseLength–maxLength) rather than pinning a short length"],"tags":["id-generation","collision","hash"],"backgroundTag":"id-generation-exhausted","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}