gastownhall/beads · error

get dependency counts: blocker rows

Error message

get dependency counts: blocker rows: %w

What it means

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.

Solutions

  1. Read the wrapped error: if it is context deadline/cancel, increase the timeout or reduce work per call.
  2. Retry the operation — transient network errors to remote backends often resolve on re-run.
  3. Reduce batch pressure by calling with fewer issue IDs per invocation (results are batched by queryBatchSize internally).
  4. Check remote Dolt server health/logs for connection resets and restart or fix the server.
  5. If persistent, switch to a local embedded Dolt database or improve network stability to the remote.

Example fix

// before: single call over hundreds of IDs on a flaky remote
counts, err := GetDependencyCountsInTx(ctx, tx, allIDs)

// after: chunk the input and retry transient failures
counts := map[string]*types.DependencyCounts{}
for _, chunk := range chunkIDs(allIDs, 100) {
    c, err := GetDependencyCountsInTx(ctx, tx, chunk)
    if isTransient(err) {
        c, err = retry(3, func() (map[string]*types.DependencyCounts, error) {
            return GetDependencyCountsInTx(ctx, tx, chunk)
        })
    }
    if err != nil { return nil, err }
    for k, v := range c { counts[k] = v }
}
Defensive patterns

Strategy: retry

Validate before calling

// Go: probe connection health before the call
if err := tx.QueryRowContext(ctx, "SELECT 1").Err(); err != nil {
    return fmt.Errorf("database unavailable: %w", err)
}

Type guard

func isTransientRowsError(err error) bool {
    if err == nil { return false }
    msg := err.Error()
    return strings.Contains(msg, "blocker rows") ||
        errors.Is(err, context.DeadlineExceeded) ||
        errors.Is(err, context.Canceled) ||
        strings.Contains(msg, "connection refused") ||
        strings.Contains(msg, "driver: bad connection")
}

Try / catch

counts, err := GetDependencyCountsInTx(ctx, tx, ids)
if isTransientRowsError(err) {
    select {
    case <-time.After(backoff):
        counts, err = GetDependencyCountsInTx(ctx, tx, ids)
    case <-ctx.Done():
        return ctx.Err()
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at internal/storage/issueops/dependency_queries.go:489

				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("get dependency counts (blockers from %s): %w", depTable, err)
			}
			for depRows.Next() {
				var id string
				var cnt int
				if err := depRows.Scan(&id, &cnt); err != nil {
					_ = depRows.Close()
					return nil, fmt.Errorf("get dependency counts: scan blocker: %w", err)
				}
				if c, ok := result[id]; ok {
					c.DependencyCount += cnt
				}
			}
			_ = depRows.Close()
			if err := depRows.Err(); err != nil {
				return nil, fmt.Errorf("get dependency counts: blocker rows: %w", err)
			}

			//nolint:gosec // G201: depTable is hardcoded and inClause contains only ? placeholders.
			blockingRows, err := tx.QueryContext(ctx, fmt.Sprintf(`
				SELECT %s AS depends_on_id, COUNT(*) as cnt
				FROM %s
				WHERE %s AND type = 'blocks'
				GROUP BY %s
			`, DepTargetExpr, depTable, depTargetIn("", inClause), DepTargetExpr), args...)
			if err != nil {
				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("get dependency counts (dependents from %s): %w", depTable, err)
			}
			for blockingRows.Next() {
				var id string
				var cnt int

View on GitHub (pinned to 71377f2769)