{"record":{"id":"6732378f16b86236","repo":"gastownhall/beads","slug":"db-labelsqlrepository-deleteallforids-rows-affect","errorCode":null,"errorMessage":"db: LabelSQLRepository.DeleteAllForIDs rows affected: %w","messagePattern":"db: LabelSQLRepository\\.DeleteAllForIDs rows affected: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/domain/db/label.go","lineNumber":222,"sourceCode":"\t\tplaceholders := make([]string, len(batch))\n\t\targs := make([]any, len(batch))\n\t\tfor i, id := range batch {\n\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\t//nolint:gosec // G201: table is one of two hardcoded constants; ? placeholders only.\n\t\tres, err := r.runner.ExecContext(ctx,\n\t\t\tfmt.Sprintf(\"DELETE FROM %s WHERE issue_id IN (%s)\", table, strings.Join(placeholders, \",\")),\n\t\t\targs...)\n\t\tif err != nil {\n\t\t\tif opts.UseWispsTable && dberrors.IsTableNotExist(err) {\n\t\t\t\treturn total, nil\n\t\t\t}\n\t\t\treturn total, fmt.Errorf(\"db: LabelSQLRepository.DeleteAllForIDs from %s: %w\", table, err)\n\t\t}\n\t\tn, err := res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn total, fmt.Errorf(\"db: LabelSQLRepository.DeleteAllForIDs rows affected: %w\", err)\n\t\t}\n\t\ttotal += int(n)\n\t}\n\treturn total, nil\n}\n\nfunc (r *labelSQLRepositoryImpl) CountAllForIDs(ctx context.Context, ids []string, opts domain.LabelOpts) (int, error) {\n\tif len(ids) == 0 {\n\t\treturn 0, nil\n\t}\n\ttable := \"labels\"\n\tif opts.UseWispsTable {\n\t\ttable = \"wisp_labels\"\n\t}\n\tcount, err := issueops.CountRowsForIssueIDsInTx(ctx, r.runner, table, ids)\n\tif err != nil {\n\t\tif opts.UseWispsTable && dberrors.IsTableNotExist(err) {\n\t\t\treturn 0, nil","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L204-L240","documentation":"LabelSQLRepository.DeleteAllForIDs executed the DELETE but res.RowsAffected() returned an error, so the number of deleted rows could not be determined. This is a driver capability/transport issue, not a failed delete.","triggerScenarios":"Calling DeleteAllForIDs against a driver/connection that does not support or fails to report rows affected (e.g. some drivers return driver.ErrSkip or transport errors).","commonSituations":"Using a driver or proxy that does not implement RowsAffected; connection interruption right after statement execution; mock/stub runners in tests lacking RowsAffected support.","solutions":["Unwrap the error to see which driver call failed","Check the storage driver supports RowsAffected for DML (upgrade driver if not)","If using a mock/test runner, implement RowsAffected in the stub result","Treat total as best-effort only if your use case does not need exact counts (may require upstream change)"],"exampleFix":"// before\n// stub result without RowsAffected\ntype fakeResult struct{}\n// after\ntype fakeResult struct{ n int64 }\nfunc (f fakeResult) RowsAffected() (int64, error) { return f.n, nil }\nfunc (f fakeResult) LastInsertId() (int64, error) { return 0, nil }","handlingStrategy":"fallback","validationCode":"// prefer drivers known to support RowsAffected; avoid stub runners in prod paths","typeGuard":null,"tryCatchPattern":"n, err := repo.DeleteAllForIDs(ctx, ids, opts)\nif err != nil && strings.Contains(err.Error(), \"rows affected\") {\n    // delete likely succeeded; treat count as unknown\n    log.Printf(\"delete ok, count unknown: %v\", err)\n    return nil\n}","preventionTips":["Use a driver whose Result implements RowsAffected","Implement RowsAffected in test mocks","Only rely on returned counts when the driver guarantees them","Upgrade driver versions with known metadata fixes"],"tags":["go","database","driver","rows-affected"],"backgroundTag":"rows-affected-unsupported","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}