{"record":{"id":"8b30e8a5c8c037c8","repo":"gastownhall/beads","slug":"delete-wisp-s-from-dependencies-w","errorCode":null,"errorMessage":"delete wisp %s from dependencies: %w","messagePattern":"delete wisp (.+?) from dependencies: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":581,"sourceCode":"\t\t\tSELECT ?\n\t\t\tUNION\n\t\t\tSELECT d.parent_id\n\t\t\tFROM ancestors a\n\t\t\tJOIN (%s) d ON d.issue_id = a.node\n\t\t)\n\t\tSELECT COUNT(*) FROM ancestors WHERE node = ?\n\t`, strings.Join(unions, \" UNION \"))\n\tvar n int\n\tif err := tx.QueryRowContext(ctx, query, node, candidate).Scan(&n); err != nil {\n\t\treturn false, err\n\t}\n\treturn n > 0, nil\n}\n\nfunc DeleteWispFromDependenciesInTx(ctx context.Context, tx *sql.Tx, wispID string) error {\n\tif _, err := tx.ExecContext(ctx,\n\t\t\"DELETE FROM dependencies WHERE depends_on_wisp_id = ?\", wispID); err != nil {\n\t\treturn fmt.Errorf(\"delete wisp %s from dependencies: %w\", wispID, err)\n\t}\n\treturn nil\n}\n\n//nolint:gosec // G201: inClause contains only ? placeholders\nfunc DeleteWispsFromDependenciesInTx(ctx context.Context, tx *sql.Tx, wispIDs []string) error {\n\tif len(wispIDs) == 0 {\n\t\treturn nil\n\t}\n\tinClause, args := buildSQLInClause(wispIDs)\n\tif _, err := tx.ExecContext(ctx,\n\t\tfmt.Sprintf(\"DELETE FROM dependencies WHERE depends_on_wisp_id IN (%s)\", inClause),\n\t\targs...); err != nil {\n\t\treturn fmt.Errorf(\"delete wisps from dependencies: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L563-L599","documentation":"Wraps a failed DELETE FROM dependencies WHERE depends_on_wisp_id = ? while DeleteWispFromDependenciesInTx removes all dependency edges that point at a deleted wisp. Called from deleteIssueRowInTx during issue deletion; a failure here aborts the deletion transaction so no dangling wisp references remain.","triggerScenarios":"Deleting an issue whose wisp is referenced in the dependencies table when the DELETE fails — DB connection loss, lock held by a concurrent reader/writer, or transaction timeout on a large dependencies table.","commonSituations":"Deleting issues during a sync/import while other connections hold locks; Dolt conflicts between the delete and concurrent dependency writes; network blips mid-transaction.","solutions":["Inspect the wrapped cause (lock timeout vs connection loss) and address it","Retry the deletion after concurrent transactions complete; the tx is atomic","Check for long-running transactions holding locks on the dependencies table","Batch large deletions to avoid long transactions timing out"],"exampleFix":"// before\nerr := issueops.DeleteWispFromDependenciesInTx(ctx, tx, wispID)\n// after: retry transient failures before aborting the whole delete\nerr := issueops.DeleteWispFromDependenciesInTx(ctx, tx, wispID)\nif err != nil && isLockWaitTimeout(err) {\n\treturn retryWithBackoff(func() error {\n\t\treturn deleteIssueWithDeps(ctx, issueID)\n\t})\n}","handlingStrategy":"retry","validationCode":"// check the wisp has references and DB is reachable before deleting\nrefs := countDepsOnWisp(ctx, db, wispID)\nif err := db.PingContext(ctx); err != nil {\n\treturn fmt.Errorf(\"db unreachable, defer delete of %s\", wispID)\n}","typeGuard":"func isWispCleanupFailure(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"delete wisp \") && strings.Contains(err.Error(), \"from dependencies\")\n}","tryCatchPattern":"err := deleteIssueRowInTx(ctx, tx, issueID)\nif err != nil && isLockWaitTimeout(err) {\n\treturn retryWithBackoff(func() error { return deleteIssue(ctx, issueID) })\n}","preventionTips":["Keep delete transactions short to avoid lock timeouts on dependencies","Retry whole transactions — cleanup is atomic with the delete","Watch for long-running concurrent transactions holding table locks","Batch large deletions to reduce transaction duration"],"tags":["database","transaction","delete"],"backgroundTag":"wisp-dependency-cleanup-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}