{"record":{"id":"ac7147549cafc28d","repo":"gastownhall/beads","slug":"db-dependencysqlrepository-wispsourceids-w","errorCode":null,"errorMessage":"db: DependencySQLRepository.WispSourceIDs: %w","messagePattern":"db: DependencySQLRepository\\.WispSourceIDs: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/dependency.go","lineNumber":959,"sourceCode":"\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 {\n\t\treturn map[string][]*types.Dependency{}, nil\n\t}\n\tout, err := issueops.GetDependencyRecordsForIssuesInTx(ctx, r.runner, issueIDs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: DependencySQLRepository.GetDependencyRecordsForIssues: %w\", err)\n\t}\n\treturn out, nil\n}\n\nfunc (r *dependencySQLRepositoryImpl) GetWispDependencyRecordsForIDs(ctx context.Context, wispIDs []string) (map[string][]*types.Dependency, error) {\n\tif len(wispIDs) == 0 {\n\t\treturn map[string][]*types.Dependency{}, nil","sourceCodeStart":941,"sourceCodeEnd":977,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/dependency.go#L941-L977","documentation":"WispSourceIDs calls issueops.WispIDSetInTx to classify which of the given IDs live in the wisp plane, in one scoped query. Like the delete/count paths, a missing wisps table inside WispIDSetInTx is treated as 'no wisps' rather than an error, so this wrapper means a genuine failure occurred: SQL error, connection loss, or scan failure.","triggerScenarios":"Calling WispSourceIDs(ctx, ids) with a non-empty ID list where WispIDSetInTx errors other than missing-table: corrupted wisp tables, driver failure, context cancellation.","commonSituations":"Dependency editors deciding whether an ID is a wisp before editing, against an unhealthy database; permission errors on wisp tables; timeouts under heavy load.","solutions":["Unwrap to see the root driver/SQL error.","Verify wisp tables exist and are healthy (run migrations/repair).","Retry transient failures with a fresh context.","Ensure IDs are valid non-empty strings; empty input short-circuits at the caller, not here."],"exampleFix":"// before\nset, err := repo.WispSourceIDs(ctx, ids)\nif err != nil { panic(err) }\n// after: degrade gracefully when wisps are unavailable\nset, err := repo.WispSourceIDs(ctx, ids)\nif err != nil {\n    log.Warn(\"wisp probe failed, assuming none\", \"err\", err)\n    set = map[string]struct{}{}\n}","handlingStrategy":"fallback","validationCode":"if len(ids) == 0 {\n    return map[string]struct{}{} // skip the DB probe entirely\n}","typeGuard":"func emptyIDs(ids []string) bool { return len(ids) == 0 }","tryCatchPattern":"set, err := repo.WispSourceIDs(ctx, ids)\nif err != nil {\n    // degrade: assume no wisps rather than failing the edit\n    log.Warn(\"wisp classification failed\", \"err\", err)\n    set = map[string]struct{}{}\n}","preventionTips":["Pass an empty slice early-exit instead of probing with no IDs.","Degrade gracefully when the wisp plane is unavailable — callers of this probe usually can.","Keep wisp tables healthy; only a missing table is tolerated as 'no wisps'."],"tags":["database","wisps","classification","wrapper"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}