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 existing

View on GitHub (pinned to 71377f2769)

Solutions

  1. Reduce batch size (queryBatchSize) so the IN clause stays within driver limits
  2. Run schema migrations to ensure the label table exists
  3. 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

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


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