{"record":{"id":"543dc739858050fe","repo":"gastownhall/beads","slug":"get-issues-by-ids-label-rows-w","errorCode":null,"errorMessage":"get issues by IDs: label rows: %w","messagePattern":"get issues by IDs: label rows: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":1085,"sourceCode":"\t\t\t\tlabelRows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t\t\t`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,\n\t\t\t\t\tpair.labelTbl, inClause), args...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"get issues by IDs: labels from %s: %w\", pair.labelTbl, err)\n\t\t\t\t}\n\t\t\t\tfor labelRows.Next() {\n\t\t\t\t\tvar issueID, label string\n\t\t\t\t\tif scanErr := labelRows.Scan(&issueID, &label); scanErr != nil {\n\t\t\t\t\t\t_ = labelRows.Close()\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"get issues by IDs: scan label: %w\", scanErr)\n\t\t\t\t\t}\n\t\t\t\t\tif issue, ok := issueMap[issueID]; ok {\n\t\t\t\t\t\tissue.Labels = append(issue.Labels, label)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_ = labelRows.Close()\n\t\t\t\tif err := labelRows.Err(); err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"get issues by IDs: label rows: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn allIssues, nil\n}\n\n// GetDependenciesWithMetadataInTx returns issues that the given issueID depends on,\n// along with the dependency type. Works within an existing transaction.\n// Queries both dependency tables to handle cross-table dependencies.\n//\n//nolint:gosec // G201: table names come from hardcoded constants\nfunc GetDependenciesWithMetadataInTx(ctx context.Context, tx DBTX, issueID string) ([]*types.IssueWithDependencyMetadata, error) {\n\ttype depMeta struct {\n\t\tdepID, depType string\n\t}\n","sourceCodeStart":1067,"sourceCodeEnd":1103,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L1067-L1103","documentation":"Wraps the labelRows.Err() check after iterating hydrated label rows in GetIssuesByIDsInTx. The scan loop completed but the result set hit a driver/connection error at the end of iteration.","triggerScenarios":"After consuming all label rows, labelRows.Err() returns non-nil — connection dropped, context canceled, or driver streaming error during iteration.","commonSituations":"Client timeout canceling the context mid-iteration; unstable connection to the DB; large label result sets iterating longer than the connection allows.","solutions":["Retry — the fetch is read-only and idempotent","Check upstream context cancellation/timeout settings","Investigate network/proxy idle timeouts if this recurs on large sets"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Ensure adequate deadline and healthy pool before batch fetches:\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nif err := db.PingContext(ctx); err != nil { return err }","typeGuard":null,"tryCatchPattern":"issues, err := GetIssuesByIDsInTx(ctx, tx, ids, nil)\nif err != nil && strings.Contains(err.Error(), \"label rows\") {\n    if errors.Is(err, context.Canceled) { return err } // don't retry cancels\n    if isTransientDBError(err) {\n        issues, err = GetIssuesByIDsInTx(ctx, tx, ids, nil) // one retry\n    }\n}","preventionTips":["Size label fetches reasonably (large ID lists iterate longer)","Set proxy/idle timeouts above worst-case query duration","Retry read-only fetches freely — they are idempotent","Check for upstream context cancellation first when diagnosing"],"tags":["database","sql","go"],"backgroundTag":"rows-iteration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}