{"record":{"id":"b620ab4e82e72a62","repo":"gastownhall/beads","slug":"db-dependencysqlrepository-cyclethroughedges-wis","errorCode":null,"errorMessage":"db: DependencySQLRepository.CycleThroughEdges (wisps): %w","messagePattern":"db: DependencySQLRepository\\.CycleThroughEdges \\(wisps\\): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/dependency.go","lineNumber":947,"sourceCode":"\t}\n\treverse := opts.Direction == domain.DepDirectionIn\n\tout, err := issueops.GetDependencyTreeInTx(ctx, r.runner, rootID, maxDepth, opts.ShowAllPaths, reverse)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: DependencySQLRepository.GetTree: %w\", err)\n\t}\n\treturn out, nil\n}\n\nfunc (r *dependencySQLRepositoryImpl) CycleThroughEdges(ctx context.Context, edges [][2]string) (string, error) {\n\tif len(edges) == 0 {\n\t\treturn \"\", nil\n\t}\n\tgraph := make(map[string][]string)\n\tif err := issueops.AppendSchedulingGraphInTx(ctx, r.runner, []string{\"dependencies\"}, graph); err != nil {\n\t\treturn \"\", fmt.Errorf(\"db: DependencySQLRepository.CycleThroughEdges: %w\", err)\n\t}\n\tif err := issueops.AppendSchedulingGraphInTx(ctx, r.runner, []string{\"wisp_dependencies\"}, graph); err != nil && !dberrors.IsTableNotExist(err) {\n\t\treturn \"\", fmt.Errorf(\"db: DependencySQLRepository.CycleThroughEdges (wisps): %w\", err)\n\t}\n\treturn issueops.CycleThroughEdgesInGraph(graph, edges), nil\n}\n\n// WispSourceIDs classifies a batch of ids by plane in one scoped query. It is\n// the proxied twin of the in-tx probe the store-backed dependency editor runs,\n// and shares its implementation so the two answer the same question — down to\n// treating a missing wisps table as \"no wisps\" rather than an error.\nfunc (r *dependencySQLRepositoryImpl) WispSourceIDs(ctx context.Context, ids []string) (map[string]struct{}, error) {\n\tset, err := issueops.WispIDSetInTx(ctx, r.runner, ids)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: DependencySQLRepository.WispSourceIDs: %w\", err)\n\t}\n\treturn set, nil\n}\n\nfunc (r *dependencySQLRepositoryImpl) GetDependencyRecordsForIssues(ctx context.Context, issueIDs []string) (map[string][]*types.Dependency, error) {\n\tif len(issueIDs) == 0 {","sourceCodeStart":929,"sourceCodeEnd":965,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/dependency.go#L929-L965","documentation":"The wisps variant of CycleThroughEdges: after the core graph loads, the code also loads wisp_dependencies into the graph. A missing wisp_dependencies table is explicitly tolerated (dberrors.IsTableNotExist is skipped), so this error means the wisp table exists but loading it failed with some other error — SQL failure, connection loss, or cancellation.","triggerScenarios":"Calling CycleThroughEdges(ctx, edges) where the \"wisp_dependencies\" table load in AppendSchedulingGraphInTx fails with an error other than table-not-exist: corrupted table, driver error, context deadline.","commonSituations":"Wisp table present but damaged after an interrupted migration; permission issues on wisp_dependencies; connection dropped mid graph-build.","solutions":["Unwrap to find the non-table-not-exist root cause.","Check integrity of wisp_dependencies; recreate/repair if corrupted.","Verify connection stability and retry.","If wisps are unused in your deployment, dropping the table cleanly restores the tolerated missing-table path."],"exampleFix":"// before: treating every wisps failure as fatal\n// after (library behavior to emulate): tolerate only table-not-exist\nif err := loadWisps(ctx, graph); err != nil && !dberrors.IsTableNotExist(err) {\n    return \"\", fmt.Errorf(\"cycle check (wisps): %w\", err)\n}","handlingStrategy":"type-guard","validationCode":"if !wispTableExists(ctx, db) {\n    // wisps plane absent: only the core graph will be consulted\n    _ = dberrors.IsTableNotExist // library tolerates this case\n}","typeGuard":"func isWispTableErr(err error) bool {\n    return strings.Contains(err.Error(), \"(wisps)\") && !dberrors.IsTableNotExist(errors.Unwrap(err))\n}","tryCatchPattern":"edge, err := repo.CycleThroughEdges(ctx, edges)\nif err != nil {\n    if dberrors.IsTableNotExist(errors.Unwrap(err)) {\n        return \"\", nil // missing wisp table is tolerated upstream, guard defensively\n    }\n    return fmt.Errorf(\"cycle check: %w\", err)\n}","preventionTips":["Do not manually delete or partially migrate wisp_dependencies — a damaged table here is fatal while a missing one is tolerated.","Keep wisp and core schemas migrated together.","Unwrap to distinguish tolerated missing-table from real wisp-table failures."],"tags":["database","wisps","cycle-detection","wrapper"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}