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

  1. Unwrap the error to see the driver cause; retry the read (reads are idempotent) on transient errors.
  2. Run schema migrations / bd doctor to confirm comments and wisp_comments tables exist.
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/85cd0a41abb81990. Report an issue: GitHub.