{"record":{"id":"71c4c35591c6a502","repo":"gastownhall/beads","slug":"get-rows-affected-w","errorCode":null,"errorMessage":"get rows affected: %w","messagePattern":"get rows affected: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":62,"sourceCode":"\t}\n\n\tif err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {\n\t\treturn fmt.Errorf(\"recompute is_blocked after delete for %s: %w\", id, err)\n\t}\n\n\treturn nil\n}\n\n//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)\nfunc deleteIssueRowInTx(ctx context.Context, tx *sql.Tx, id string, isWisp bool) error {\n\tissueTable, _, _, _ := WispTableRouting(isWisp)\n\tresult, err := tx.ExecContext(ctx, fmt.Sprintf(\"DELETE FROM %s WHERE id = ?\", issueTable), id)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"delete issue from %s: %w\", issueTable, err)\n\t}\n\trows, err := result.RowsAffected()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get rows affected: %w\", err)\n\t}\n\tif rows == 0 {\n\t\t// Wrap the sentinel so callers can errors.Is(..., storage.ErrNotFound),\n\t\t// matching GetIssue/UpdateIssue. The storage conformance suite asserts\n\t\t// this parity across not-found paths.\n\t\treturn fmt.Errorf(\"%w: issue %s\", storage.ErrNotFound, id)\n\t}\n\t// Journal the delete in the same transaction. This worker backs single\n\t// deletes (DeleteIssueInTx) and the per-wisp branch of the bulk delete\n\t// (DeleteResolvedSetInTx); the bulk regular-issue branch journals its own\n\t// ids directly. The rows==0 return above is what keeps this\n\t// actually-deleted-only. The delete plumbing (storage.DeleteIssue and the\n\t// bulk/cascade resolvers) carries no actor, so the row records none.\n\tif err := RecordDeleteInTx(ctx, tx, id, \"\"); err != nil {\n\t\treturn err\n\t}\n\tif isWisp {\n\t\tif err := DeleteWispFromDependenciesInTx(ctx, tx, id); err != nil {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L44-L80","documentation":"deleteIssueRowInTx wraps a failure of result.RowsAffected() after the DELETE statement succeeded. This is rare: it means the driver could not report how many rows the statement removed (some drivers/pooled connections can't provide rows-affected). It precedes the not-found check.","triggerScenarios":"Calling any delete path when the database/driver's RowsAffected call errors after a successful DELETE — driver-level result metadata failure, very old or mismatched driver version.","commonSituations":"Mismatched or outdated Dolt SQL driver version that doesn't fully implement result metadata; connection in a degraded state where the result handle is invalid.","solutions":["Inspect the wrapped error; it is a driver result-metadata failure.","Upgrade the dolthub/driver dependency to a current version.","Retry the delete in a fresh transaction/connection.","If reproducible, report to the driver: the DELETE succeeded, only the affected-count read failed."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := storage.DeleteIssue(ctx, db, id); err != nil {\n\tif isResultMetadataErr(err) { // RowsAffected failed after a successful DELETE\n\t\t// verify outcome by re-reading the row, then proceed or retry\n\t}\n\treturn err\n}","preventionTips":["Keep the dolthub/driver dependency up to date.","Re-verify row existence after this error before assuming the delete failed.","Report persistent occurrences to the driver project."],"tags":["database","driver","error-wrapping"],"backgroundTag":"driver-result-metadata-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}