{"record":{"id":"0bb58ce91ade3fdb","repo":"gastownhall/beads","slug":"get-dependency-counts-blocker-rows-w","errorCode":null,"errorMessage":"get dependency counts: blocker rows: %w","messagePattern":"get dependency counts: blocker rows: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":489,"sourceCode":"\t\t\t\tif optionalBlockedTable(depTable) && isTableNotExistError(err) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts (blockers from %s): %w\", depTable, err)\n\t\t\t}\n\t\t\tfor depRows.Next() {\n\t\t\t\tvar id string\n\t\t\t\tvar cnt int\n\t\t\t\tif err := depRows.Scan(&id, &cnt); err != nil {\n\t\t\t\t\t_ = depRows.Close()\n\t\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts: scan blocker: %w\", err)\n\t\t\t\t}\n\t\t\t\tif c, ok := result[id]; ok {\n\t\t\t\t\tc.DependencyCount += cnt\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ = depRows.Close()\n\t\t\tif err := depRows.Err(); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts: blocker rows: %w\", err)\n\t\t\t}\n\n\t\t\t//nolint:gosec // G201: depTable is hardcoded and inClause contains only ? placeholders.\n\t\t\tblockingRows, err := tx.QueryContext(ctx, fmt.Sprintf(`\n\t\t\t\tSELECT %s AS depends_on_id, COUNT(*) as cnt\n\t\t\t\tFROM %s\n\t\t\t\tWHERE %s AND type = 'blocks'\n\t\t\t\tGROUP BY %s\n\t\t\t`, DepTargetExpr, depTable, depTargetIn(\"\", inClause), DepTargetExpr), args...)\n\t\t\tif err != nil {\n\t\t\t\tif optionalBlockedTable(depTable) && isTableNotExistError(err) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"get dependency counts (dependents from %s): %w\", depTable, err)\n\t\t\t}\n\t\t\tfor blockingRows.Next() {\n\t\t\t\tvar id string\n\t\t\t\tvar cnt int","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L471-L507","documentation":"Returned by GetDependencyCountsInTx after iterating the 'blocked-by counts' result set when rows.Err() reports that the underlying driver encountered an error mid-iteration (network drop, query cancellation, driver failure). The rows were being read fine, but the connection or query failed before the result set was exhausted.","triggerScenarios":"Calling GetDependencyCountsInTx (via HydrateReadyRowInTx) when the database connection drops or the context is cancelled while streaming GROUP BY results from dependencies/wisp_dependencies; remote Dolt server closing the connection mid-response.","commonSituations":"Flaky network to a remote Dolt backend; server-side timeouts on large IN batches; context deadline exceeded during a slow query; Dolt server restart during a long bd operation.","solutions":["Read the wrapped error: if it is context deadline/cancel, increase the timeout or reduce work per call.","Retry the operation — transient network errors to remote backends often resolve on re-run.","Reduce batch pressure by calling with fewer issue IDs per invocation (results are batched by queryBatchSize internally).","Check remote Dolt server health/logs for connection resets and restart or fix the server.","If persistent, switch to a local embedded Dolt database or improve network stability to the remote."],"exampleFix":"// before: single call over hundreds of IDs on a flaky remote\ncounts, err := GetDependencyCountsInTx(ctx, tx, allIDs)\n\n// after: chunk the input and retry transient failures\ncounts := map[string]*types.DependencyCounts{}\nfor _, chunk := range chunkIDs(allIDs, 100) {\n    c, err := GetDependencyCountsInTx(ctx, tx, chunk)\n    if isTransient(err) {\n        c, err = retry(3, func() (map[string]*types.DependencyCounts, error) {\n            return GetDependencyCountsInTx(ctx, tx, chunk)\n        })\n    }\n    if err != nil { return nil, err }\n    for k, v := range c { counts[k] = v }\n}","handlingStrategy":"retry","validationCode":"// Go: probe connection health before the call\nif err := tx.QueryRowContext(ctx, \"SELECT 1\").Err(); err != nil {\n    return fmt.Errorf(\"database unavailable: %w\", err)\n}","typeGuard":"func isTransientRowsError(err error) bool {\n    if err == nil { return false }\n    msg := err.Error()\n    return strings.Contains(msg, \"blocker rows\") ||\n        errors.Is(err, context.DeadlineExceeded) ||\n        errors.Is(err, context.Canceled) ||\n        strings.Contains(msg, \"connection refused\") ||\n        strings.Contains(msg, \"driver: bad connection\")\n}","tryCatchPattern":"counts, err := GetDependencyCountsInTx(ctx, tx, ids)\nif isTransientRowsError(err) {\n    select {\n    case <-time.After(backoff):\n        counts, err = GetDependencyCountsInTx(ctx, tx, ids)\n    case <-ctx.Done():\n        return ctx.Err()\n    }\n}","preventionTips":["Always pass a context with a realistic timeout for remote backends.","Chunk large ID lists (e.g. 100–500 per call) to shorten streamed results.","Monitor remote Dolt server uptime and restart-on-crash (systemd/k8s).","Configure DB connection pool keepalives and max lifetime below proxy idle timeouts.","Retry with exponential backoff on transient network errors."],"tags":["database","network","row-iteration","context-cancelled"],"backgroundTag":"database-connection-dropped","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}