{"record":{"id":"60ebc546b0c9dcb3","repo":"gastownhall/beads","slug":"wisp-cascade-traversal-discovered-over-d-issues","errorCode":null,"errorMessage":"wisp cascade traversal discovered over %d issues; aborting","messagePattern":"wisp cascade traversal discovered over (.+?) issues; aborting","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":374,"sourceCode":"\tif len(ids) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tconst maxResults = 10000\n\tconst batchSize = 50\n\n\tseen := make(map[string]bool, len(ids))\n\tfor _, id := range ids {\n\t\tseen[id] = true\n\t}\n\n\ttoProcess := make([]string, len(ids))\n\tcopy(toProcess, ids)\n\tdiscovered := make(map[string]bool)\n\n\tfor len(toProcess) > 0 {\n\t\tif len(seen) > maxResults {\n\t\t\treturn discovered, fmt.Errorf(\"wisp cascade traversal discovered over %d issues; aborting\", maxResults)\n\t\t}\n\n\t\tend := batchSize\n\t\tif end > len(toProcess) {\n\t\t\tend = len(toProcess)\n\t\t}\n\t\tbatch := toProcess[:end]\n\t\ttoProcess = toProcess[end:]\n\n\t\tplaceholders, args := buildSQLInClause(batch)\n\t\trows, err := tx.QueryContext(ctx,\n\t\t\tfmt.Sprintf(`SELECT issue_id FROM wisp_dependencies WHERE %s IN (%s)`, DepTargetExpr, placeholders),\n\t\t\targs...)\n\t\tif err != nil {\n\t\t\treturn discovered, fmt.Errorf(\"query wisp dependents: %w\", err)\n\t\t}\n\n\t\tfor rows.Next() {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L356-L392","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, 100) // aborts: over 100 issues\n// after\nids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, 10000)","handlingStrategy":"validation","validationCode":"// size the bound to your graph before traversal\nconst safeMax = 50000\nids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, safeMax)\nif err != nil && strings.Contains(err.Error(), \"cascade traversal discovered over\") {\n    return fmt.Errorf(\"cascade too large; triage partial set and raise bound\")\n}","typeGuard":null,"tryCatchPattern":"ids, err := store.FindWispDependentsRecursive(ctx, tx, rootID, maxResults)\nif err != nil && strings.Contains(err.Error(), \"aborting\") {\n    partial := ids // returned despite error; triage before retrying with a bigger bound\n    return escalateCascap(partial, err)\n}","preventionTips":["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."],"tags":["graph","cascade","limit","wisp"],"backgroundTag":"traversal-limit-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}