gastownhall/beads · error
descendants: %w
Error message
descendants: %w
What it means
GetDescendants wraps a failure from scanIDSrcPage, which reads the (id, src) rows produced by the descendants CTE and splits them into issue IDs and wisp IDs. The scan loop can fail on rows.Scan (a column type mismatch) or on rows.Err() (a mid-iteration connection/engine failure). Since the query only projects two string columns, this usually means the stream broke mid-read.
Source
Thrown at internal/storage/domain/db/issue_descendants.go:85
return nil, fmt.Errorf("descendants: wisps filter: %w", err)
}
}
issuePred := buildDescendantsPred("issues", "i", "issue_matches", issueWhereClauses, issueArgs)
var wispPred predBundle
if walkWisps {
wispPred = buildDescendantsPred("wisps", "w", "wisp_matches", wispWhereClauses, wispArgs)
}
cte, allArgs := buildDescendantsCTE(rootID, walkWisps, issuePred, wispPred)
rows, err := r.runner.QueryContext(ctx, cte, allArgs...)
if err != nil {
return nil, fmt.Errorf("descendants: query: %w", err)
}
page, err := scanIDSrcPage(rows)
if err != nil {
return nil, fmt.Errorf("descendants: %w", err)
}
issuesByID, err := r.fetchIssuesByIDs(ctx, page.issueIDs, issuesFilterTables, filter)
if err != nil {
return nil, fmt.Errorf("descendants: hydrate issues: %w", err)
}
var wispsByID map[string]*types.Issue
if len(page.wispIDs) > 0 {
wispsByID, err = r.fetchIssuesByIDs(ctx, page.wispIDs, wispsFilterTables, filter)
if err != nil && !dberrors.IsTableNotExist(err) {
return nil, fmt.Errorf("descendants: hydrate wisps: %w", err)
}
}
return reassembleBySrc(page.ordered, issuesByID, wispsByID), nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped inner error: connection errors warrant retrying the whole GetDescendants call
- Reduce result size with filter.Limit/MaxRows so the scan completes within connection limits
- Verify Dolt server stability (logs, memory) if failures recur on large walks
- If it is a scan-type error, ensure the bd binary and database schema are the same version
Example fix
// before
issues, err := repo.GetDescendants(ctx, root, types.IssueFilter{})
// after: bound the walk so the result stream stays small
issues, err := repo.GetDescendants(ctx, root, types.IssueFilter{MaxRows: 10000}) Defensive patterns
Strategy: retry
Try / catch
issues, err := repo.GetDescendants(ctx, rootID, filter)
if err != nil && strings.HasSuffix(strings.TrimPrefix(err.Error(), "descendants: "), "rows") || strings.Contains(err.Error(), "scan") {
// result stream broke mid-read: retry the whole call
} Prevention
- Bound walks with MaxRows/Limit so result streams stay small
- Avoid network paths with aggressive idle timeouts during large reads
- Keep driver and server versions consistent
- Retry idempotent reads on stream failure
When it happens
Trigger: Calling GetDescendants when the result stream from the recursive CTE fails while being read: connection dropped between QueryContext and full row consumption, driver scan error, or ctx canceled during iteration.
Common situations: Large hierarchies whose scan outlasts a network idle timeout; Dolt server killed mid-result; driver/driver-version mismatch producing unexpected column types.
Related errors
- ErrScan
- failed to scan dependency keys: %w
- %s: %w
- failed to scan federation peer: %w
- failed to scan comment: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9c622f3970637751.
Report an issue: GitHub.