{"record":{"id":"aff1defa1b0bb86b","repo":"gastownhall/beads","slug":"get-labels-for-issues-from-s-w","errorCode":null,"errorMessage":"get labels for issues from %s: %w","messagePattern":"get labels for issues from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/labels.go","lineNumber":113,"sourceCode":"//nolint:gosec // G201: labelTable is \"labels\" or \"wisp_labels\" (hardcoded by callers).\nfunc getLabelsIntoFromTable(ctx context.Context, tx DBTX, labelTable string, ids []string, result map[string][]string) error {\n\tfor start := 0; start < len(ids); start += queryBatchSize {\n\t\tend := start + queryBatchSize\n\t\tif end > len(ids) {\n\t\t\tend = len(ids)\n\t\t}\n\t\tbatch := ids[start:end]\n\t\tplaceholders := make([]string, len(batch))\n\t\targs := make([]any, len(batch))\n\t\tfor i, id := range batch {\n\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,\n\t\t\tlabelTable, strings.Join(placeholders, \",\")), args...)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"get labels for issues from %s: %w\", labelTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar issueID, label string\n\t\t\tif err := rows.Scan(&issueID, &label); err != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn fmt.Errorf(\"get labels for issues: scan: %w\", err)\n\t\t\t}\n\t\t\tresult[issueID] = append(result[issueID], label)\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"get labels for issues: rows: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// AddLabelInTx adds a label to an issue and records an event within an existing","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/labels.go#L95-L131","documentation":"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.","triggerScenarios":"Calling GetLabelsForIssuesInTx when the query fails — table missing, too many placeholders for the driver, or connection failure during execution.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if len(issueIDs) == 0 { return map[string][]string{}, nil }\nif len(issueIDs) > 500 { return errors.New(\"batch too large; split into chunks to avoid placeholder/packet limits\") }","typeGuard":null,"tryCatchPattern":"m, err := issueops.GetLabelsForIssuesInTx(ctx, tx, ids)\nif err != nil {\n    if strings.Contains(err.Error(), \"max_allowed_packet\") || strings.Contains(err.Error(), \"placeholder\") {\n        // retry with smaller batches\n    }\n    return err\n}","preventionTips":["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"],"tags":["database","sql","labels","batch-query"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}