{"record":{"id":"495f36fcf6a1081b","repo":"gastownhall/beads","slug":"get-comments-from-s-w","errorCode":null,"errorMessage":"get comments from %s: %w","messagePattern":"get comments from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":152,"sourceCode":"func getCommentsForIDsInto(ctx context.Context, tx *sql.Tx, table string, ids []string, result map[string][]*types.Comment) error {\n\tfor start := 0; start < len(ids); start += queryBatchSize {\n\t\tend := start + queryBatchSize\n\t\tif end > len(ids) {\n\t\t\tend = len(ids)\n\t\t}\n\t\tbatch := ids[start:end]\n\t\tplaceholders, args := buildSQLInClause(batch)\n\n\t\tquery := fmt.Sprintf(`\n\t\t\tSELECT id, issue_id, author, text, created_at\n\t\t\tFROM %s\n\t\t\tWHERE issue_id IN (%s)\n\t\t\tORDER BY issue_id, created_at ASC, id ASC\n\t\t`, table, placeholders)\n\n\t\trows, err := tx.QueryContext(ctx, query, args...)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"get comments from %s: %w\", table, err)\n\t\t}\n\n\t\tfor rows.Next() {\n\t\t\tvar c types.Comment\n\t\t\tif err := rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt); err != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn fmt.Errorf(\"scan comment: %w\", err)\n\t\t\t}\n\t\t\tresult[c.IssueID] = append(result[c.IssueID], &c)\n\t\t}\n\t\tif err := rows.Err(); err != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn err\n\t\t}\n\t\t_ = rows.Close()\n\t}\n\treturn nil\n}","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L134-L170","documentation":"getCommentsForIDsInto batches a SELECT of comments from the comments (or wisp_comments) table for a set of issue IDs and wraps any driver-level query failure with \"get comments from <table>:\". This fires when the SQL statement itself fails to execute — bad SQL, missing table, or a driver/transaction-level error.","triggerScenarios":"tx.QueryContext fails on the comments/wisp_comments table: table missing (schema not migrated), SQLite/dolt driver error, transaction already aborted or rolled back by a prior step, or context canceled mid-query.","commonSituations":"Running bd against a database created by an older version that lacks the wisp_comments table; a rolled-back or timed-out transaction reused by the caller; context deadline exceeded during a large bulk comment read.","solutions":["Verify the DB schema is current (run the tool's migration/upgrade path) so comments and wisp_comments exist","Inspect the wrapped driver error for the root cause (no such table vs context canceled vs busy)","Ensure the *sql.Tx passed in is still valid and uncommitted/unrolled-back when this runs","Increase the context timeout or retry the whole transaction if it was a transient busy/lock error"],"exampleFix":"// before: reusing an aborted tx\nrows, err := tx.QueryContext(ctx, query, args...) // fails: transaction has been rolled back\n// after: check tx state / use a fresh transaction\ntx, err := db.BeginTx(ctx, nil)\nif err != nil { return nil, err }\nrows, err := tx.QueryContext(ctx, query, args...)","handlingStrategy":"try-catch","validationCode":"// Go has no pre-call validation; ensure schema and tx health before the call\nif tx == nil { return errors.New(\"nil transaction\") }\n// optionally: verify table exists via a lightweight query before bulk reads","typeGuard":"// errors.As to extract the driver error\nvar driverErr *sqlite.Error\nif errors.As(err, &driverErr) { /* inspect driverErr.Code */ }","tryCatchPattern":"err := GetCommentsForIssuesInTx(ctx, tx, ids)\nif err != nil {\n    var derr interface{ Error() string }\n    if errors.Is(err, context.DeadlineExceeded) { /* retry whole tx */ }\n    return fmt.Errorf(\"bulk comment fetch failed: %w\", err)\n}","preventionTips":["Keep the database schema migrated to the current version","Always pass a live, uncommitted transaction and a context with adequate timeout","Retry full transactions on transient busy/locked errors instead of partial retries"],"tags":["database","sqlite","sql-query"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}