gastownhall/beads · error

db: CommentSQLRepository.ListByIssueIDs

Error message

db: CommentSQLRepository.ListByIssueIDs: %w

What it means

ListByIssueIDs issues a SELECT of id, issue_id, author, text, created_at from `comments`/`wisp_comments` for the given issue IDs, ordered for deterministic grouping. This error wraps the QueryContext call failing before any rows are produced — the query itself was rejected (missing table, bad connection, canceled context). It is also propagated to callers of IterByIssueID.

Solutions

  1. Chunk large issueIDs slices (e.g. 500–1000 per query) to avoid IN-clause/placeholder limits.
  2. Ensure migrations created the selected table; match UseWispsTable to the schema you actually have.
  3. Inspect the wrapped driver error for connectivity vs schema causes (dberrors.IsTableNotExist).
  4. Retry on transient connection errors.

Example fix

// before: one query with unbounded ID list
comments, err := repo.ListByIssueIDs(ctx, allThousandIDs, opts)

// after: chunk the ID list
for chunk := range slices.Chunk(allThousandIDs, 500) {
    batch, err := repo.ListByIssueIDs(ctx, chunk, opts)
    if err != nil { return err }
    // merge batch
}
Defensive patterns

Strategy: validation

Validate before calling

const maxInClause = 500
if len(issueIDs) > maxInClause {
    return fmt.Errorf("split %d issue IDs into chunks of %d", len(issueIDs), maxInClause)
}

Type guard

func isListQueryError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "ListByIssueIDs:") &&
        !strings.Contains(err.Error(), "scan:") && !strings.Contains(err.Error(), "rows:")
}

Try / catch

comments, err := repo.ListByIssueIDs(ctx, ids, opts)
if err != nil && isListQueryError(err) {
    if dberrors.IsTableNotExist(err) { migrate(); comments, err = repo.ListByIssueIDs(ctx, ids, opts) }
    if err != nil { return nil, err }
}

Prevention

When it happens

Trigger: Calling ListByIssueIDs (or IterByIssueID) with a huge issueIDs list overflowing placeholders; querying `wisp_comments` when the wisp schema is absent; canceled context; broken connection.

Common situations: Bulk dashboards passing thousands of issue IDs at once; databases created before wisp tables were introduced; connection pool exhaustion under load.

Related errors


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

Appendix: source

Thrown at internal/storage/domain/db/comment.go:90

		return result, nil
	}
	placeholders := make([]string, len(issueIDs))
	args := make([]any, len(issueIDs))
	for i, id := range issueIDs {
		placeholders[i] = "?"
		args[i] = id
	}
	table := pickCommentTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	q := fmt.Sprintf(`
		SELECT id, issue_id, author, text, created_at
		FROM %s
		WHERE issue_id IN (%s)
		ORDER BY issue_id, created_at ASC, id ASC
	`, table, strings.Join(placeholders, ","))
	rows, err := r.runner.QueryContext(ctx, q, args...)
	if err != nil {
		return nil, fmt.Errorf("db: CommentSQLRepository.ListByIssueIDs: %w", err)
	}
	defer rows.Close()

	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("db: CommentSQLRepository.ListByIssueIDs: scan: %w", err)
		}
		cc := c
		result[c.IssueID] = append(result[c.IssueID], &cc)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("db: CommentSQLRepository.ListByIssueIDs: rows: %w", err)
	}
	return result, nil
}

func (r *commentSQLRepositoryImpl) IterByIssueID(ctx context.Context, issueID string, opts domain.CommentOpts) (storage.Iter[types.Comment], error) {

View on GitHub (pinned to 71377f2769)