{"record":{"id":"2060b8229713a204","repo":"gastownhall/beads","slug":"count-wisp-dependencies-w","errorCode":null,"errorMessage":"count wisp dependencies: %w","messagePattern":"count wisp dependencies: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":217,"sourceCode":"func DeleteResolvedSetInTx(ctx context.Context, tx *sql.Tx, set DeletionSet, dryRun bool) (*types.DeleteIssuesResult, error) {\n\tresult := &types.DeleteIssuesResult{}\n\tif len(set.All) == 0 {\n\t\treturn result, nil\n\t}\n\n\tdeletedSet := make(map[string]bool, len(set.All))\n\tfor _, id := range set.All {\n\t\tdeletedSet[id] = true\n\t}\n\n\tvar depsCount, labelsCount, eventsCount int\n\tvar err error\n\tif depsCount, err = CountRowsForIssueIDsInTx(ctx, tx, \"dependencies\", set.RegularIDs); err != nil {\n\t\treturn nil, fmt.Errorf(\"count dependencies: %w\", err)\n\t}\n\twispDepsCount, err := CountRowsForIssueIDsInTx(ctx, tx, \"wisp_dependencies\", set.WispIDs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"count wisp dependencies: %w\", err)\n\t}\n\tdepsCount += wispDepsCount\n\n\tif labelsCount, err = CountRowsForIssueIDsInTx(ctx, tx, \"labels\", set.RegularIDs); err != nil {\n\t\treturn nil, fmt.Errorf(\"count labels: %w\", err)\n\t}\n\twispLabelsCount, err := CountRowsForIssueIDsInTx(ctx, tx, \"wisp_labels\", set.WispIDs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"count wisp labels: %w\", err)\n\t}\n\tlabelsCount += wispLabelsCount\n\n\tif eventsCount, err = CountRowsForIssueIDsInTx(ctx, tx, \"events\", set.RegularIDs); err != nil {\n\t\treturn nil, fmt.Errorf(\"count events: %w\", err)\n\t}\n\twispEventsCount, err := CountRowsForIssueIDsInTx(ctx, tx, \"wisp_events\", set.WispIDs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"count wisp events: %w\", err)","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L199-L235","documentation":"DeleteResolvedSetInTx aborts a batch delete when it cannot count rows in the \"wisp_dependencies\" table for the wisp IDs being deleted. The error wraps the underlying SQL/driver error from CountRowsForIssueIDsInTx, so the real cause (connection loss, unknown table, context cancellation, syntax) is in the wrapped chain. It exists to compute the dependency count reported in a dry-run delete result.","triggerScenarios":"Calling DeleteResolvedSetInTx (directly or via DeleteIssuesInTx/DeleteInTx) when the wisp_dependencies table is missing, corrupted, locked, or the underlying DB connection/transaction has failed. Also triggered by context cancellation or deadline expiry during the count query.","commonSituations":"Running against a database migrated from an older beads version that predates the wisp_dependencies table; a Dolt server restart or dropped connection mid-transaction; schema corruption; an embedded-mode driver without wisp tables.","solutions":["Inspect the wrapped error with errors.Unwrap/%+v to find the driver-level cause (unknown table vs connection vs context deadline).","Run the beads schema migration so wisp_dependencies exists (or confirm the driver supports wisp tables).","Retry the delete; a transient connection or lock failure usually clears on a new transaction.","Check Dolt server health and network connectivity if the wrapped error is connection-related."],"exampleFix":"// before\nresult, err := store.DeleteInTx(ctx, tx, ids)\nif err != nil {\n    return fmt.Errorf(\"delete failed: %w\", err)\n}\n// after\nresult, err := store.DeleteInTx(ctx, tx, ids)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return fmt.Errorf(\"delete timed out, retrying: %w\", err)\n    }\n    return fmt.Errorf(\"delete failed: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// before deleting, verify wisp tables are reachable\nvar n int\nif err := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM wisp_dependencies LIMIT 1`).Scan(&n); err != nil {\n    return fmt.Errorf(\"wisp_dependencies unreadable, run migrations: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"res, err := store.DeleteInTx(ctx, tx, ids)\nif err != nil {\n    if isTransient(err) { // e.g. driver.ErrBadConn / deadline\n        res, err = store.DeleteInTx(ctx, tx, ids) // fresh tx, safe to retry\n    }\n    if err != nil {\n        return fmt.Errorf(\"delete failed: %w\", err)\n    }\n}","preventionTips":["Run schema migrations/`bd doctor` before programmatic deletes.","Use a context timeout proportional to the delete batch size.","Retry deletes on transient driver errors; deletes abort atomically before mutating."],"tags":["go","storage","transaction","sql","batch-delete"],"backgroundTag":"sql-count-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}