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
- Unwrap the driver error; if it is context.DeadlineExceeded, increase the deadline or shrink the ID batch
- Retry the whole partition in a fresh transaction — it is read-only and safe to repeat
- Check network stability to the Dolt server (keepalives, proxy timeouts) if this recurs
- 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
- Give partition calls generous deadlines proportional to batch size
- Enable TCP keepalives / raise proxy idle timeouts for Dolt connections
- Avoid cancelling parent contexts mid-query
- Retry the whole read-only partition — it is safe to repeat
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
- wisp id set: rows: %w
- db: ChildCounterSQLRepository.NextChildID: rows: %w
- db: CommentSQLRepository.CountsByIssueIDs: rows: %w
- db: CommentSQLRepository.ListByIssueIDs: rows: %w
- db: RawSQL Query: rows: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9b02a9730feda3e6.
Report an issue: GitHub.