{"record":{"id":"5a1de92f659d20ff","repo":"gastownhall/beads","slug":"failed-to-generate-unique-id-for-prefix-q-after-l","errorCode":null,"errorMessage":"failed to generate unique ID for prefix %q after lengths %d..%d with 10 nonces each","messagePattern":"failed to generate unique ID for prefix %q after lengths (.+?)\\.\\.(.+?) with 10 nonces each","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1642,"sourceCode":"\t}\n\tbaseLength := ComputeAdaptiveLength(count, cfg)\n\tif baseLength > cfg.MaxLength {\n\t\tbaseLength = cfg.MaxLength\n\t}\n\n\tfor length := baseLength; length <= cfg.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\t\t\texists, err := u.issueRepo.Exists(ctx, candidate, tableOpts)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\treturn candidate, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\", fmt.Errorf(\"failed to generate unique ID for prefix %q after lengths %d..%d with 10 nonces each\", prefix, baseLength, cfg.MaxLength)\n}\n\nfunc (u *issueUseCaseImpl) CloseIssue(ctx context.Context, id string, params CloseIssueParams, actor string) (CloseIssueResult, error) {\n\treturn u.close(ctx, id, params, actor, false)\n}\n\nfunc (u *issueUseCaseImpl) CloseWisp(ctx context.Context, id string, params CloseIssueParams, actor string) (CloseIssueResult, error) {\n\treturn u.close(ctx, id, params, actor, true)\n}\n\n// CloseIssueChecked closes an issue through the shared guarded close path.\nfunc (u *issueUseCaseImpl) CloseIssueChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force bool) (CloseIssueResult, error) {\n\treturn u.closeChecked(ctx, id, params, actor, force, false)\n}\n\n// CloseWispChecked is the wisp twin of CloseIssueChecked.\nfunc (u *issueUseCaseImpl) CloseWispChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force bool) (CloseIssueResult, error) {\n\treturn u.closeChecked(ctx, id, params, actor, force, true)","sourceCodeStart":1624,"sourceCodeEnd":1660,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1624-L1660","documentation":"Hash-ID minting exhausted all retries: for every length from the adaptive base length up to cfg.MaxLength, 10 nonce variants were generated and every candidate already existed in the target table. This means the hash space for that prefix/title/actor/createdAt combination is effectively saturated — astronomically unlikely in normal use.","triggerScenarios":"mintTopLevelID loop (issue.go:1630-1641) completing without returning: Exists() true for all candidates — e.g. adversarial duplication (identical title/description/actor/CreatedAt reused), pathological AdaptiveIDConfig with a tiny MaxLength, or a bug freezing CreatedAt across retries.","commonSituations":"Script mass-creating issues with identical titles and a pinned CreatedAt; misconfigured MaxLength set to 1-2 chars; automated import re-running with deterministic inputs.","solutions":["Check AdaptiveIDConfig MaxLength — raise it if it was shrunk below sane values (e.g. 6+).","Ensure issue.CreatedAt is set to the real timestamp and not pinned/zero so nonces hash differently.","Vary the issue Title/Description or mint with an explicit ID for bulk duplicates.","If truly colliding, inspect CountForPrefix/ComputeAdaptiveLength behavior for your issue volume."],"exampleFix":"// before: deterministic retries collide\nissue.CreatedAt = fixedTime // same for all inserts\n\n// after\nissue.CreatedAt = time.Now().UTC() // per-record timestamp varies the hash","handlingStrategy":"retry","validationCode":"cfg, err := cfgRepo.GetAdaptiveIDConfig(ctx)\nif err == nil && cfg.MaxLength < 6 {\n    return errors.New(\"adaptive MaxLength too small; collision risk high — raise it\")\n}\nif issue.CreatedAt.IsZero() {\n    issue.CreatedAt = time.Now().UTC()\n}","typeGuard":null,"tryCatchPattern":"var errCollision = errors.New(\"failed to generate unique ID\")\nif err != nil && strings.Contains(err.Error(), \"failed to generate unique ID\") {\n    issue.CreatedAt = time.Now().UTC() // re-randomize hash inputs\n    return mintTopLevelID(ctx, issue, actor, useWisp) // one retry\n}","preventionTips":["Never pin CreatedAt across bulk inserts; let each record get its own timestamp.","Keep AdaptiveIDConfig.MaxLength at the default (don't shrink below 6).","Use explicit IDs for bulk imports with duplicate titles.","Monitor for large counts per prefix; adaptive lengths should grow automatically."],"tags":["id-generation","collision","hash"],"backgroundTag":"id-generation-collision","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}