{"record":{"id":"09cd508b190e3b82","repo":"gastownhall/beads","slug":"rewrite-refs-s-w","errorCode":null,"errorMessage":"rewrite refs %s: %w","messagePattern":"rewrite refs (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue_delete.go","lineNumber":425,"sourceCode":"\t\t\tupdates := map[string]any{}\n\t\t\tif re.MatchString(conn.Description) {\n\t\t\t\tupdates[\"description\"] = re.ReplaceAllString(conn.Description, replacement)\n\t\t\t}\n\t\t\tif conn.Notes != \"\" && re.MatchString(conn.Notes) {\n\t\t\t\tupdates[\"notes\"] = re.ReplaceAllString(conn.Notes, replacement)\n\t\t\t}\n\t\t\tif conn.Design != \"\" && re.MatchString(conn.Design) {\n\t\t\t\tupdates[\"design\"] = re.ReplaceAllString(conn.Design, replacement)\n\t\t\t}\n\t\t\tif conn.AcceptanceCriteria != \"\" && re.MatchString(conn.AcceptanceCriteria) {\n\t\t\t\tupdates[\"acceptance_criteria\"] = re.ReplaceAllString(conn.AcceptanceCriteria, replacement)\n\t\t\t}\n\t\t\tif len(updates) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\topts := IssueTableOpts{UseWispsTable: isWisp[connID]}\n\t\t\tif err := u.issueRepo.Update(ctx, connID, updates, actor, opts); err != nil {\n\t\t\t\treturn len(touched), fmt.Errorf(\"rewrite refs %s: %w\", connID, err)\n\t\t\t}\n\t\t\ttouched[connID] = true\n\t\t\tif desc, ok := updates[\"description\"].(string); ok {\n\t\t\t\tconn.Description = desc\n\t\t\t}\n\t\t\tif notes, ok := updates[\"notes\"].(string); ok {\n\t\t\t\tconn.Notes = notes\n\t\t\t}\n\t\t\tif design, ok := updates[\"design\"].(string); ok {\n\t\t\t\tconn.Design = design\n\t\t\t}\n\t\t\tif ac, ok := updates[\"acceptance_criteria\"].(string); ok {\n\t\t\t\tconn.AcceptanceCriteria = ac\n\t\t\t}\n\t\t}\n\t}\n\treturn len(touched), nil\n}","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue_delete.go#L407-L443","documentation":"Wraps a failure from issueRepo.Update while rewriting textual references to a deleted issue inside a connected issue's description/notes/design/acceptance_criteria. After deletion, references like `bd-123` are replaced with `[deleted:bd-123]`; if the row update fails, the whole deleteMany aborts with this error naming the failing connected issue ID.","triggerScenarios":"deleteMany where a surviving neighbor issue contains a textual reference to a deleted ID and the UPDATE on that neighbor (issues or wisps table depending on isWisp) fails — DB lock, connection error, context canceled, or constraint violation.","commonSituations":"Deleting issues concurrently with another writer holding row locks; database going away mid-batch; updating a wisp row in a store whose wisp table is in a bad state; long-running deletes hitting driver timeouts.","solutions":["Read the wrapped driver error: fix locks/connectivity first (the failing issue ID is embedded in the message).","Retry the delete once the database is idle; avoid concurrent bd writers on the same store.","Check whether the target row still exists — a concurrently deleted neighbor makes the UPDATE fail.","If context cancellation is the cause, run the delete with a dedicated long-timeout context.","Manually resolve the reference in the named issue, then retry the delete."],"exampleFix":"// before: two concurrent bd processes delete/update at once\n//   error: rewrite refs bd-42: database is locked\n// after: serialize mutations (single writer, or an external lock)\nlock := acquireStoreLock()\ndefer lock.Release()\ndeleteErr := deleter.DeleteMany(ctx, ids)","handlingStrategy":"try-catch","validationCode":"// ensure the target issues still exist before the delete triggers rewrites\nfor _, id := range connectedIDs {\n    if _, err := usecase.GetIssue(ctx, id); err != nil {\n        return fmt.Errorf(\"connected issue %s unavailable: %w\", id, err)\n    }\n}","typeGuard":"func isRewriteRefsErr(err error) (string, bool) {\n    if err == nil { return \"\", false }\n    var id string\n    if _, scanErr := fmt.Sscanf(err.Error(), \"rewrite refs %s:\", &id); scanErr == nil {\n        return id, true\n    }\n    return \"\", false\n}","tryCatchPattern":"err := usecase.DeleteMany(ctx, ids)\nif err != nil && strings.Contains(err.Error(), \"rewrite refs \") {\n    // extract failing issue id from message, check locks/row state, retry\n    cause := errors.Unwrap(err)\n    return fmt.Errorf(\"delete aborted during ref rewrite: %w\", cause)\n}","preventionTips":["Serialize deletes — never run two mutation commands on the same store concurrently","Use a long-timeout context for deletes that touch many connected issues","Handle 'already exists / not found' driver errors idempotently in retry loops","Keep batches small so a mid-batch storage failure is cheap to retry"],"tags":["storage","database","update","delete","referential-integrity"],"backgroundTag":"database-update-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}