gastownhall/beads · warning
wisp cascade traversal discovered over %d issues; aborting
Error message
wisp cascade traversal discovered over %d issues; aborting
What it means
FindWispDependentsRecursiveInTx performs a breadth-first traversal of wisp_dependencies and refuses to continue once the discovered set exceeds maxResults. This is a deliberate safety guard against runaway cascades (cycles or very large dependency graphs); it aborts with this error rather than exhausting memory or scanning the whole table.
Source
Thrown at internal/storage/issueops/bulk_ops.go:374
if len(ids) == 0 {
return nil, nil
}
const maxResults = 10000
const batchSize = 50
seen := make(map[string]bool, len(ids))
for _, id := range ids {
seen[id] = true
}
toProcess := make([]string, len(ids))
copy(toProcess, ids)
discovered := make(map[string]bool)
for len(toProcess) > 0 {
if len(seen) > maxResults {
return discovered, fmt.Errorf("wisp cascade traversal discovered over %d issues; aborting", maxResults)
}
end := batchSize
if end > len(toProcess) {
end = len(toProcess)
}
batch := toProcess[:end]
toProcess = toProcess[end:]
placeholders, args := buildSQLInClause(batch)
rows, err := tx.QueryContext(ctx,
fmt.Sprintf(`SELECT issue_id FROM wisp_dependencies WHERE %s IN (%s)`, DepTargetExpr, placeholders),
args...)
if err != nil {
return discovered, fmt.Errorf("query wisp dependents: %w", err)
}
for rows.Next() {View on GitHub (pinned to 71377f2769)
Solutions
- Increase maxResults to a value covering the expected cascade size and retry.
- Break the traversal into per-branch calls with smaller roots to stay under the limit.
- Inspect the dependency graph for accidental cycles and remove them.
- Handle the partial `discovered` set (it is returned with the error) to triage before re-running with a bigger bound.
Example fix
// before ids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, 100) // aborts: over 100 issues // after ids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, 10000)
Defensive patterns
Strategy: validation
Validate before calling
// size the bound to your graph before traversal
const safeMax = 50000
ids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, safeMax)
if err != nil && strings.Contains(err.Error(), "cascade traversal discovered over") {
return fmt.Errorf("cascade too large; triage partial set and raise bound")
} Try / catch
ids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, maxResults)
if err != nil && strings.Contains(err.Error(), "aborting") {
partial := ids // returned despite error; triage before retrying with a bigger bound
return escalateCascap(partial, err)
} Prevention
- Set maxResults comfortably above your expected dependency fan-out.
- Audit the dependency graph periodically for accidental cycles/hubs.
- Cascade per-branch instead of from a single mega-root.
When it happens
Trigger: Calling FindWispDependentsRecursiveInTx with an ID whose transitive dependent closure exceeds maxResults — a hub wisp that thousands of others depend on, or a dependency cycle feeding back into already-visited nodes at scale.
Common situations: Bulk-deleting or cascading a top-level wisp in a large project; accidental dependency cycle creating unbounded traversal; calling with a very low maxResults while the graph legitimately grew.
Related errors
- ExternalDoltConfig: TLSKey set without TLSCert
- loading dependencies: %w
- determining wisp status for %s: %w
- failed to close wisp root %s: %w
- failed to clear ephemeral flag on root %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/60ebc546b0c9dcb3.
Report an issue: GitHub.