gastownhall/beads · error

is_blocked rows from %s: %w

Error message

is_blocked rows from %s: %w

What it means

Wraps rows.Err() after iterating is_blocked rows in readIsBlockedIntoFromTable. Scan can succeed per-row yet the iterator can still fail; this check catches transport/query errors surfaced at the end of iteration (driver disconnect, query killed, server error mid-stream). The library throws it so partial batch results are never returned as if complete.

Source

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

		}
		for rows.Next() {
			var id string
			var b int
			if err := rows.Scan(&id, &b); err != nil {
				_ = rows.Close()
				return fmt.Errorf("scan is_blocked from %s: %w", table, err)
			}
			// Keep the first-seen (issues) value and skip any later (wisps)
			// duplicate, so the batch is_blocked matches per-row IsBlocked.
			if seen[id] {
				continue
			}
			seen[id] = true
			blocked[id] = b != 0
		}
		_ = rows.Close()
		if err := rows.Err(); err != nil {
			return fmt.Errorf("is_blocked rows from %s: %w", table, err)
		}
	}
	return nil
}

// scanDependencyRow scans a single dependency row from a *sql.Rows.
func scanDependencyRow(rows *sql.Rows) (*types.Dependency, error) {
	var dep types.Dependency
	var createdAt sql.NullTime
	var metadata, threadID sql.NullString

	if err := rows.Scan(&dep.IssueID, &dep.DependsOnID, &dep.Type, &createdAt, &dep.CreatedBy, &metadata, &threadID); err != nil {
		return nil, fmt.Errorf("scan dependency: %w", err)
	}

	if createdAt.Valid {
		dep.CreatedAt = createdAt.Time
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the operation; rows.Err() here is usually transient (connection or context issue).
  2. Check context deadlines: increase timeout or pass a context with adequate budget for large batches.
  3. Verify Dolt server/network stability and connection pool settings.
  4. Reduce batch size so each query completes well within timeouts.

Example fix

// before: no deadline, fails mid-iteration
blocked, err := ops.IsBlockedBatchInTx(ctx, tx, ids)
// after: bounded, retryable context
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
blocked, err := ops.IsBlockedBatchInTx(ctx, tx, ids)
if err != nil { return retry(ctx, ...) }
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil {
	return fmt.Errorf("context already done before batch is_blocked: %w", err)
}
if err := txPlaceholder.PingContext(ctx); err != nil {
	return fmt.Errorf("connection dead before batch: %w", err)
}

Type guard

func isTransientRowsErr(err error) bool {
	return errors.Is(err, context.DeadlineExceeded) ||
		errors.Is(err, context.Canceled) ||
		errors.Is(err, driver.ErrBadConn)
}

Try / catch

err := ops.IsBlockedBatchInTx(ctx, tx, ids)
for attempt := 0; isTransientRowsErr(err) && attempt < 3; attempt++ {
	time.Sleep(backoff(attempt))
	err = ops.IsBlockedBatchInTx(ctx, tx, ids)
}
if err != nil { return err }

Prevention

When it happens

Trigger: IsBlockedBatchInTx runs, rows iterate normally, but the Dolt connection drops or the query is cancelled/timed out before the result set is fully drained, so rows.Err() is non-nil.

Common situations: Long batch is_blocked queries over flaky connections; context timeout cancelling the query mid-iteration; Dolt server restart during a large batch check.

Related errors


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