{"record":{"id":"1179e86b772b3ad1","repo":"gastownhall/beads","slug":"db-commentsqlrepository-listbyissueids-rows-w","errorCode":null,"errorMessage":"db: CommentSQLRepository.ListByIssueIDs: rows: %w","messagePattern":"db: CommentSQLRepository\\.ListByIssueIDs: rows: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/comment.go","lineNumber":103,"sourceCode":"\t\tWHERE issue_id IN (%s)\n\t\tORDER BY issue_id, created_at ASC, id ASC\n\t`, table, strings.Join(placeholders, \",\"))\n\trows, err := r.runner.QueryContext(ctx, q, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: %w\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar c types.Comment\n\t\tif err := rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: scan: %w\", err)\n\t\t}\n\t\tcc := c\n\t\tresult[c.IssueID] = append(result[c.IssueID], &cc)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: rows: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (r *commentSQLRepositoryImpl) IterByIssueID(ctx context.Context, issueID string, opts domain.CommentOpts) (storage.Iter[types.Comment], error) {\n\tbulk, err := r.ListByIssueIDs(ctx, []string{issueID}, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn storage.NewSliceIter(bulk[issueID]), nil\n}\n\nfunc (r *commentSQLRepositoryImpl) Insert(ctx context.Context, issueID, author, text string, opts domain.CommentOpts) (*types.Comment, error) {\n\t// Live add: advance past the issue's newest comment so a burst inside one\n\t// second still reads back in write order (issueops.NextLiveCommentTime).\n\t// InsertRecord honors a supplied CreatedAt verbatim, which is what keeps\n\t// imported comments on their original timestamps.\n\tstamp, err := issueops.NextLiveCommentTime(ctx, r.runner, pickCommentTable(opts.UseWispsTable), issueID, time.Now())","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/comment.go#L85-L121","documentation":"ListByIssueIDs calls rows.Err() after the scan loop to detect errors during row streaming; this wrapper surfaces it. The entire per-issue comment grouping is discarded on failure, and IterByIssueID propagates it directly. Callers get no partial data.","triggerScenarios":"Calling ListByIssueIDs/IterByIssueID when the connection drops, the context is canceled, or the server aborts the query while comment rows are being streamed.","commonSituations":"Request-scoped deadlines expiring on large comment fetches; remote Dolt server over an unstable network; server-side query kill due to max_execution_time.","solutions":["Check errors.Is(err, context.DeadlineExceeded) and raise the timeout for large fetches.","Retry; the operation is read-only and safe to repeat.","Reduce result size with pagination/chunking of issue IDs.","Verify connection pool MaxLifetime is shorter than the server's idle timeout."],"exampleFix":"// before: streaming a huge comment set inside a short request context\nbulk, err := repo.ListByIssueIDs(ctx, ids, opts)\n\n// after: bounded retry on iteration failure\nfor attempt := 0; attempt < 3; attempt++ {\n    bulk, err = repo.ListByIssueIDs(ctx, ids, opts)\n    if err == nil || !errors.Is(err, context.DeadlineExceeded) { break }\n    time.Sleep(time.Second * time.Duration(attempt+1))\n}","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil { return fmt.Errorf(\"pre-list context check: %w\", err) }","typeGuard":"func isListRowsError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"ListByIssueIDs: rows:\")\n}","tryCatchPattern":"bulk, err := repo.ListByIssueIDs(ctx, ids, opts)\nfor attempt := 1; attempt <= 3 && isListRowsError(err); attempt++ {\n    time.Sleep(time.Duration(attempt) * time.Second)\n    bulk, err = repo.ListByIssueIDs(ctx, ids, opts)\n}","preventionTips":["Use longer deadlines for comment-heavy fetches","Prefer chunked reads over monolithic queries","Watch server max_execution_time settings"],"tags":["database","sql","rows-iteration","go"],"backgroundTag":"query-iteration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}