{"record":{"id":"4f3e7a16f5b60082","repo":"gastownhall/beads","slug":"generating-linear-milestone-epic-id-w","errorCode":null,"errorMessage":"generating Linear milestone epic ID: %w","messagePattern":"generating Linear milestone epic ID: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/linear.go","lineNumber":638,"sourceCode":"\t\t\t\treturn \"\", fmt.Errorf(\"updating Linear milestone epic %s: %w\", existing.ID, err)\n\t\t\t}\n\t\t}\n\t\treturn ref, nil\n\t}\n\n\texternalRef := ref\n\tepic := &types.Issue{\n\t\tTitle:       title,\n\t\tDescription: description,\n\t\tStatus:      types.StatusOpen,\n\t\tPriority:    2,\n\t\tIssueType:   types.TypeEpic,\n\t\tExternalRef: &externalRef,\n\t\tMetadata:    metadata,\n\t}\n\tif generateID != nil {\n\t\tif err := generateID(ctx, epic); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"generating Linear milestone epic ID: %w\", err)\n\t\t}\n\t}\n\tif err := st.CreateIssue(ctx, epic, actor); err != nil {\n\t\treturn \"\", fmt.Errorf(\"creating Linear milestone epic %q: %w\", title, err)\n\t}\n\treturn ref, nil\n}\n\nfunc findLinearMilestoneEpic(ctx context.Context, st storage.Storage, ref, milestoneID, title string) (*types.Issue, error) {\n\tif existing, err := st.GetIssueByExternalRef(ctx, ref); err == nil {\n\t\treturn existing, nil\n\t} else if !errors.Is(err, storage.ErrNotFound) {\n\t\treturn nil, err\n\t}\n\n\tissues, err := st.SearchIssues(ctx, \"\", types.IssueFilter{})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"searching local issues for Linear milestone %s: %w\", milestoneID, err)","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/linear.go#L620-L656","documentation":"When creating a new Linear milestone epic, an optional generateID callback assigns the issue ID; any error it returns is wrapped as \"generating Linear milestone epic ID: %w\". This happens before CreateIssue, so nothing has been persisted yet.","triggerScenarios":"ensureLinearMilestoneEpic creates a fresh epic for a milestone not yet in beads, and the injected generateID function (custom ID generation, e.g. prefix/sequence logic) returns an error — sequence exhaustion, storage-backed ID allocation failure.","commonSituations":"Custom ID generator backed by a DB counter that fails or times out; misconfigured generateID in test harnesses; ID prefix collisions configured in workspace settings.","solutions":["Inspect the wrapped error from your generateID implementation","Verify the ID allocation backend (counter/sequence storage) is healthy","Retry the sync once ID generation works; no epic was created on failure so retry is clean"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify the generator works before starting the sync\nif generateID != nil {\n    probe := &types.Issue{Title: \"probe\"}\n    if err := generateID(ctx, probe); err != nil {\n        return \"\", fmt.Errorf(\"ID generator unhealthy: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := generateID(ctx, epic); err != nil {\n    var gerr *IDGenError\n    if errors.As(err, &gerr) { /* fall back to default ID scheme or abort */ }\n    return \"\", fmt.Errorf(\"generating Linear milestone epic ID: %w\", err)\n}","preventionTips":["Test custom generateID implementations in CI before production syncs","Keep ID sequence/counter storage on the same healthy backend as the issue store","Pass nil generateID to use default ID generation if custom logic is unnecessary"],"tags":["linear","id-generation","sync"],"backgroundTag":"id-generation-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}