gastownhall/beads · error
get labels for issues from %s: %w
Error message
get labels for issues from %s: %w
What it means
getLabelsIntoFromTable returns this when the batched SELECT of labels for multiple issues fails at query-execution time against the given label table. The function builds an IN (...) clause over issue IDs and is used by GetLabelsForIssuesInTx/GetLabelsForIssuesFromTableInTx for bulk hydration. The wrapped table name identifies which label table ('labels' or 'wisp_labels') the query targeted.
Source
Thrown at internal/storage/issueops/labels.go:113
//nolint:gosec // G201: labelTable is "labels" or "wisp_labels" (hardcoded by callers).
func getLabelsIntoFromTable(ctx context.Context, tx DBTX, labelTable string, ids []string, result map[string][]string) error {
for start := 0; start < len(ids); start += queryBatchSize {
end := start + queryBatchSize
if end > len(ids) {
end = len(ids)
}
batch := ids[start:end]
placeholders := make([]string, len(batch))
args := make([]any, len(batch))
for i, id := range batch {
placeholders[i] = "?"
args[i] = id
}
rows, err := tx.QueryContext(ctx, fmt.Sprintf(
`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,
labelTable, strings.Join(placeholders, ",")), args...)
if err != nil {
return fmt.Errorf("get labels for issues from %s: %w", labelTable, err)
}
for rows.Next() {
var issueID, label string
if err := rows.Scan(&issueID, &label); err != nil {
_ = rows.Close()
return fmt.Errorf("get labels for issues: scan: %w", err)
}
result[issueID] = append(result[issueID], label)
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return fmt.Errorf("get labels for issues: rows: %w", err)
}
}
return nil
}
// AddLabelInTx adds a label to an issue and records an event within an existingView on GitHub (pinned to 71377f2769)
Solutions
- Reduce batch size (queryBatchSize) so the IN clause stays within driver limits
- Run schema migrations to ensure the label table exists
- Inspect the wrapped driver error for packet-size or connection errors and adjust max_allowed_packet or retry
Defensive patterns
Strategy: validation
Validate before calling
if len(issueIDs) == 0 { return map[string][]string{}, nil }
if len(issueIDs) > 500 { return errors.New("batch too large; split into chunks to avoid placeholder/packet limits") } Try / catch
m, err := issueops.GetLabelsForIssuesInTx(ctx, tx, ids)
if err != nil {
if strings.Contains(err.Error(), "max_allowed_packet") || strings.Contains(err.Error(), "placeholder") {
// retry with smaller batches
}
return err
} Prevention
- Chunk issue-ID lists to a few hundred per batch
- Ensure schema migrations have created both label tables
- Raise max_allowed_packet on MySQL/Dolt if large batches are required
- Prefer the batched API over per-ID queries on remote backends
When it happens
Trigger: Calling GetLabelsForIssuesInTx when the query fails — table missing, too many placeholders for the driver, or connection failure during execution.
Common situations: Very large issue-ID batches exceeding driver placeholder limits or max_allowed_packet on MySQL/Dolt; schema versions missing the label table; remote backend connection drops.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- get labels from %s: %w
- get issues by IDs: labels from %s: %w
- get issue labels: %w
- get labels: %w
- add label: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/aff1defa1b0bb86b.
Report an issue: GitHub.