{"record":{"id":"fe9a888652c24e4b","repo":"gastownhall/beads","slug":"searching-local-issues-for-linear-milestone-s-w","errorCode":null,"errorMessage":"searching local issues for Linear milestone %s: %w","messagePattern":"searching local issues for Linear milestone (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/linear.go","lineNumber":656,"sourceCode":"\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)\n\t}\n\tfor _, issue := range issues {\n\t\tif issueHasLinearMilestoneID(issue, milestoneID) {\n\t\t\treturn issue, nil\n\t\t}\n\t}\n\n\tfor _, issue := range issues {\n\t\tif issue.IssueType != types.TypeEpic || !strings.EqualFold(strings.TrimSpace(issue.Title), title) {\n\t\t\tcontinue\n\t\t}\n\t\tref := \"\"\n\t\tif issue.ExternalRef != nil {\n\t\t\tref = strings.TrimSpace(*issue.ExternalRef)\n\t\t}\n\t\tif ref == \"\" {\n\t\t\treturn issue, nil\n\t\t}","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/linear.go#L638-L674","documentation":"findLinearMilestoneEpic falls back to a full SearchIssues scan when the milestone epic isn't found by external ref, matching issues by embedded milestone ID metadata. If that search fails, the error wraps the storage failure with the milestone ID. This is the slow-path dedup lookup during Linear milestone sync.","triggerScenarios":"Linear milestone sync where GetIssueByExternalRef returns not-found (and not storage.ErrNotFound edge cases) and the fallback st.SearchIssues(ctx, \"\", IssueFilter{}) errors — storage outage, large-workspace query failure, corrupted index.","commonSituations":"Database unavailable during import; very large workspaces where the unfiltered search times out; storage backend misconfiguration in the workspace.","solutions":["Inspect the wrapped storage error and restore DB connectivity","Re-run the sync; the lookup is read-only and safe to retry","If searches repeatedly fail on large workspaces, ensure the epic's external ref is set so the fast GetIssueByExternalRef path succeeds and the scan fallback is never needed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check storage health before the scan-heavy fallback\nif err := st.Ping(ctx); err != nil {\n    return nil, fmt.Errorf(\"storage unavailable before milestone lookup: %w\", err)\n}","typeGuard":"func isNotFoundErr(err error) bool { return errors.Is(err, storage.ErrNotFound) }","tryCatchPattern":"issues, err := st.SearchIssues(ctx, \"\", types.IssueFilter{})\nif err != nil {\n    if isTransient(err) { /* backoff and retry once */ }\n    return nil, fmt.Errorf(\"searching local issues for Linear milestone %s: %w\", milestoneID, err)\n}","preventionTips":["Ensure milestone epics always carry external refs so the fast lookup path is used","Schedule syncs during low-load windows for very large workspaces","Retry read-only lookups freely — they have no side effects"],"tags":["linear","storage","search","sync","fallback-scan"],"backgroundTag":"storage-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}