{"record":{"id":"8cfe88a3d97a75f7","repo":"gastownhall/beads","slug":"capture-dependency-edges-for-rename-s-s-w","errorCode":null,"errorMessage":"capture dependency edges for rename %s -> %s: %w","messagePattern":"capture dependency edges for rename (.+?) -> (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":250,"sourceCode":"\n\tif err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {\n\t\treturn int(rowsAffected), fmt.Errorf(\"recompute is_blocked after source-repo delete: %w\", err)\n\t}\n\n\treturn int(rowsAffected), nil\n}\n\n//nolint:gosec // G201: table names are hardcoded\nfunc UpdateIssueIDInTx(ctx context.Context, tx *sql.Tx, oldID, newID string, issue *types.Issue, actor string) error {\n\t// Capture the edges under the OLD id before the rename rewrites them; they\n\t// are what the journal replays as a remove/re-add pair around the identity\n\t// change.\n\tvar renameEdges []journalDependencyEdge\n\tif journalEnabled(ctx, tx) {\n\t\tvar err error\n\t\trenameEdges, err = dependencyEdgesForIssueIDsInTx(ctx, tx, []string{oldID})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"capture dependency edges for rename %s -> %s: %w\", oldID, newID, err)\n\t\t}\n\t}\n\tif IsActiveWispInTx(ctx, tx, oldID) {\n\t\tif err := updateWispIDInTx(ctx, tx, oldID, newID, issue, actor); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if err := updateIssueIDInTx(ctx, tx, oldID, newID, issue, actor); err != nil {\n\t\treturn err\n\t}\n\treturn recordRenameInJournal(ctx, tx, oldID, newID, actor, renameEdges)\n}\n\n// recordRenameInJournal replays a rename as the operations a consumer can apply\n// without understanding identity changes: drop the old edges, delete the old\n// bead, create the new one, re-add the edges under the new id.\nfunc recordRenameInJournal(ctx context.Context, tx DBTX, oldID, newID, actor string, edges []journalDependencyEdge) error {\n\tfor _, edge := range edges {\n\t\tif err := RecordDepEventInTx(ctx, tx, EventDepRemove, edge.source, edge.kind, edge.target, edge.metadata, actor); err != nil {","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L232-L268","documentation":"This error wraps a failure while snapshotting the dependency edges of an issue immediately before its ID is renamed inside a transaction. The library captures all edges referencing oldID first so the journal can replay the rename correctly; if reading those edges fails, the rename is aborted to avoid an unrecoverable journal entry. It is only raised when journaling is enabled (journalEnabled).","triggerScenarios":"Calling UpdateIssueIDInTx (or UpdateIssueID) while the Dolt/journal is enabled and dependencyEdgesForIssueIDsInTx fails: the underlying SELECT over the dependencies table errors (connection drop, table locked, schema mismatch).","commonSituations":"Database connectivity loss mid-rename; a migration left the dependencies table in an unexpected schema; concurrent Dolt push/pull locking the table; running against an aux/journal store that is temporarily unavailable.","solutions":["Check database connectivity and retry the rename; the transaction is rolled back so it is safe to retry.","Verify the dependencies table schema matches the installed migration set (bd doctor / migrations up to date).","If journaling is misconfigured, fix or temporarily disable the journal and re-run the rename.","Inspect the wrapped error (%w) for the driver-level cause (lock timeout, IO error) and address that root cause."],"exampleFix":"// before\nerr := store.UpdateIssueID(ctx, oldID, newID, issue, actor) // fails on flaky Dolt connection\n// after\nif err := pingStore(ctx, store); err != nil {\n    return fmt.Errorf(\"store unavailable, retry rename later: %w\", err)\n}\nerr := store.UpdateIssueID(ctx, oldID, newID, issue, actor)","handlingStrategy":"try-catch","validationCode":"if err := store.Ping(ctx); err != nil {\n    return fmt.Errorf(\"storage unavailable before rename: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := store.UpdateIssueID(ctx, oldID, newID, issue, actor); err != nil {\n    if strings.Contains(err.Error(), \"capture dependency edges for rename\") {\n        // journal/DB read failed; safe to retry after connectivity check\n        return retryLater(err)\n    }\n    return err\n}","preventionTips":["Ping or health-check the storage backend before bulk renames.","Keep journal/aux schema migrations current.","Avoid renames during concurrent Dolt push/pull windows."],"tags":["database","rename","journal","dependencies"],"backgroundTag":"dependency-edge-capture-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}