gastownhall/beads · error
get issue comments from %s: %w
Error message
get issue comments from %s: %w
What it means
GetIssueCommentsInTx fails when the SELECT of comments for an issue (from comments or wisp_comments, chosen by wisp routing) returns a driver error. The failure is wrapped as "get issue comments from %s: %w" naming which table was queried. A missing issue is not this error — routing falls back to comments and returns an empty list; this is a query/infrastructure failure only.
Source
Thrown at internal/storage/issueops/comments.go:31
// GetIssueCommentsInTx retrieves comments for an issue within an existing
// transaction. Automatically routes to wisp_comments if the ID is an active wisp.
//
//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)
func GetIssueCommentsInTx(ctx context.Context, tx DBTX, issueID string) ([]*types.Comment, error) {
table := "comments"
if IsActiveWispInTx(ctx, tx, issueID) {
table = "wisp_comments"
}
rows, err := tx.QueryContext(ctx, fmt.Sprintf(`
SELECT id, issue_id, author, text, created_at
FROM %s
WHERE issue_id = ?
ORDER BY created_at ASC, id ASC
`, table), issueID)
if err != nil {
return nil, fmt.Errorf("get issue comments from %s: %w", table, err)
}
defer rows.Close()
var comments []*types.Comment
for rows.Next() {
var c types.Comment
if err := rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt); err != nil {
return nil, fmt.Errorf("get issue comments: scan: %w", err)
}
comments = append(comments, &c)
}
return comments, rows.Err()
}
// Comment page-read tuning. Mirrors the EventsSince keyset clamp: an unbounded
// page defeats the purpose of paging a long thread, so a non-positive limit
// falls back to the default and any larger request is capped.
const (View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error to see the driver cause; retry the read (reads are idempotent) on transient errors.
- Run schema migrations / bd doctor to confirm comments and wisp_comments tables exist.
- Increase the context timeout for large threads on slow links.
Example fix
// before
comments, err := store.GetIssueComments(ctx, id)
// after
comments, err := store.GetIssueComments(ctx, id)
if err != nil && isTransient(err) { // driver.ErrBadConn etc.
comments, err = store.GetIssueComments(ctx, id) // idempotent retry
} Defensive patterns
Strategy: retry
Try / catch
comments, err := store.GetIssueComments(ctx, id)
if err != nil {
if isTransientDB(err) { // ErrBadConn, deadline, lock timeout
comments, err = store.GetIssueComments(ctx, id) // reads are idempotent
}
if err != nil { return fmt.Errorf("read comments: %w", err) }
} Prevention
- Retry idempotent reads once on transient driver errors before failing.
- Keep bd binary and database schema versions aligned to avoid missing wisp_comments.
- Use adequate context timeouts for large threads; avoid cancelling mid-scan.
When it happens
Trigger: Calling GetIssueComments (or HydrateIssueOperationResult) when the connection drops, the context is cancelled mid-query, or the comments/wisp_comments table is missing/locked; schema drift after a partial migration.
Common situations: Reading comments on a remote Dolt server over a flaky link; running an older bd binary against a newer schema (or vice versa) so wisp_comments does not exist; concurrent DOLT schema changes during the read.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- %s: %w
- failed to query orphaned dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/85cd0a41abb81990.
Report an issue: GitHub.