gastownhall/beads · error

partition wisp ids: rows: %w

Error message

partition wisp ids: rows: %w

What it means

After draining the result rows of the wisps IN-query, PartitionWispIDsInTx checks rows.Err(). This error wraps any error accumulated during iteration that was not surfaced by Scan — most commonly a lost connection or context cancellation while streaming rows. The built wispSet is discarded; no partial partition is returned.

Source

Thrown at internal/storage/issueops/wisp_routing.go:176

			args...)
		if qErr != nil {
			// Wisps table may not exist yet on older schemas — treat as "no wisps".
			if isTableNotExistError(qErr) {
				return nil, append([]string(nil), ids...), nil
			}
			return nil, nil, fmt.Errorf("partition wisp ids: %w", qErr)
		}
		for rows.Next() {
			var id string
			if scanErr := rows.Scan(&id); scanErr != nil {
				_ = rows.Close()
				return nil, nil, fmt.Errorf("partition wisp ids: scan: %w", scanErr)
			}
			wispSet[id] = struct{}{}
		}
		_ = rows.Close()
		if rowsErr := rows.Err(); rowsErr != nil {
			return nil, nil, fmt.Errorf("partition wisp ids: rows: %w", rowsErr)
		}
	}

	for _, id := range ids {
		if _, ok := wispSet[id]; ok {
			wispIDs = append(wispIDs, id)
		} else {
			permIDs = append(permIDs, id)
		}
	}
	return wispIDs, permIDs, nil
}

// WispTableRouting returns the appropriate issue, label, event, and dependency
// table names based on whether the ID is an active wisp. Call IsActiveWispInTx
// first to determine isWisp.
func WispTableRouting(isWisp bool) (issueTable, labelTable, eventTable, depTable string) {
	if isWisp {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the driver error; if it is context.DeadlineExceeded, increase the deadline or shrink the ID batch
  2. Retry the whole partition in a fresh transaction — it is read-only and safe to repeat
  3. Check network stability to the Dolt server (keepalives, proxy timeouts) if this recurs
  4. Confirm the caller is not cancelling the context early (e.g. an HTTP client timeout shorter than the query time)

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 2*time.Second) // too tight
wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)
// after
ctx, cancel := context.WithTimeout(ctx, 30*time.Second) // sized for batch
wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)
if err != nil { return retryPartition(context.Background(), tx, ids) }
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil { return err }
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < 5*time.Second {
	return errors.New("deadline too tight for wisp partition")
}

Try / catch

wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)
if err != nil {
	if strings.Contains(err.Error(), "rows:") && errors.Is(err, context.DeadlineExceeded) {
		return retryPartitionWithLargerDeadline(ctx, ids)
	}
	return err
}

Prevention

When it happens

Trigger: Calling PartitionWispIDsInTx (directly or via GetCommentsForIssuesInTx, GetCommentCountsInTx, ResolveDeletionSetInTx, GetDependencyRecordsForIssuesInTx, GetBlockingInfoForIssuesInTx, GetLabelsForIssuesInTx) when the rows iterator finishes in an error state — connection terminated mid-stream, driver I/O error, or ctx cancelled after the last successful Scan.

Common situations: Dolt server restart or load-balancer idle timeout severing a long result stream; caller's request deadline expiring mid-iteration; flaky network to a remote Dolt backend during bulk operations.

Related errors


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