{"record":{"id":"a43be45c03704fb8","repo":"gastownhall/beads","slug":"delete-rewrite-text-references-w","errorCode":null,"errorMessage":"delete: rewrite text references: %w","messagePattern":"delete: rewrite text references: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue_delete.go","lineNumber":209,"sourceCode":"\t}\n\tif _, err := u.eventsRepo.DeleteAllForIDs(ctx, wispIDs, RecordEventOpts{UseWispsTable: true}); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop wisp events: %w\", err)\n\t}\n\n\tissuesDeleted, err := u.issueRepo.DeleteByIDs(ctx, regularIDs, IssueTableOpts{})\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop issue rows: %w\", err)\n\t}\n\twispsDeleted, err := u.issueRepo.DeleteByIDs(ctx, wispIDs, IssueTableOpts{UseWispsTable: true})\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"delete: drop wisp rows: %w\", err)\n\t}\n\tresult.DeletedCount = issuesDeleted + wispsDeleted\n\n\tif params.UpdateTextReferences && len(connected) > 0 {\n\t\trefs, err := u.rewriteTextReferences(ctx, allIDs, connected, connectedIsWisp, actor)\n\t\tif err != nil {\n\t\t\treturn result, fmt.Errorf(\"delete: rewrite text references: %w\", err)\n\t\t}\n\t\tresult.ReferencesUpdated = refs\n\t}\n\n\tif err := u.issueRepo.RecomputeIsBlocked(ctx, affectedIssues, affectedWisps); err != nil {\n\t\treturn result, fmt.Errorf(\"delete: recompute is_blocked: %w\", err)\n\t}\n\n\treturn result, nil\n}\n\n// externalDependents finds the direct dependents of each id in ids that are\n// not themselves in ids, across both the issue and wisp dependency tables.\n// The result maps deletion-set id -> external dependent ids (unsorted).\nfunc (u *issueUseCaseImpl) externalDependents(ctx context.Context, ids []string) (map[string][]string, error) {\n\tidSet := make(map[string]bool, len(ids))\n\tfor _, id := range ids {\n\t\tidSet[id] = true","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue_delete.go#L191-L227","documentation":"Wraps failure of rewriteTextReferences, the optional step (DeleteIssuesParams.UpdateTextReferences) that rewrites textual references (e.g. in descriptions) of issues connected to the deleted set. Unlike the row deletes this happens after rows are already removed, so a failure leaves deletions applied but references possibly stale; the result and error are still returned.","triggerScenarios":"Calling DeleteIssue/DeleteIssues with UpdateTextReferences:true when scanning or updating text of connected issues fails (SQL error on read/update, missing columns/tables, connection loss).","commonSituations":"Very large connected graphs making the rewrite slow and prone to timeouts; schema drift in embedded DB; context cancelled mid-rewrite; concurrent edits to the referencing issues.","solutions":["Inspect the wrapped cause for the specific SQL/read error and fix it.","Retry the delete with UpdateTextReferences:true, or verify/re-run reference rewriting separately since rows are already deleted.","Increase the context deadline for large connected sets.","Reduce the deletion batch size to shrink the connected-issue graph."],"exampleFix":"// before\nres, err := store.DeleteIssues(ctx, ids, DeleteIssuesParams{UpdateTextReferences: true})\n// after\nres, err := store.DeleteIssues(ctx, ids, DeleteIssuesParams{UpdateTextReferences: true})\nif err != nil && strings.Contains(err.Error(), \"rewrite text references\") {\n    log.Printf(\"rows deleted but references may be stale: %v\", err) // re-run rewrite manually\n}","handlingStrategy":"try-catch","validationCode":"// gate the flag on expected graph size; skip when the connected graph is huge\nif params.UpdateTextReferences && len(ids) > largeThreshold {\n    params.UpdateTextReferences = false // rewrite references in a separate controlled pass\n}","typeGuard":null,"tryCatchPattern":"res, err := store.DeleteIssues(ctx, ids, opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"delete: rewrite text references\") {\n        // rows ARE deleted; schedule a reference-rewrite backfill instead of retrying the delete\n        scheduleReferenceRewrite(ids)\n    }\n}","preventionTips":["Only enable UpdateTextReferences when the connected graph is small enough to rewrite within your context deadline.","Remember rows are already gone when this error fires — recover by re-running the rewrite, not the delete.","Use long context deadlines when reference rewriting is enabled.","Avoid concurrent edits to referencing issues during deletion."],"tags":["storage","database","delete","text-references"],"backgroundTag":"reference-rewrite-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}