{"record":{"id":"372ba9c6abf07e10","repo":"gastownhall/beads","slug":"db-dependencysqlrepository-getdependencyrecordsfo","errorCode":null,"errorMessage":"db: DependencySQLRepository.GetDependencyRecordsForIssues: %w","messagePattern":"db: DependencySQLRepository\\.GetDependencyRecordsForIssues: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/dependency.go","lineNumber":970,"sourceCode":"// 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\n\t}\n\tout, err := issueops.GetDependencyRecordsForIssuesFromTableInTx(ctx, r.runner, \"wisp_dependencies\", wispIDs)\n\tif err != nil {\n\t\tif dberrors.IsTableNotExist(err) {\n\t\t\treturn map[string][]*types.Dependency{}, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"db: DependencySQLRepository.GetWispDependencyRecordsForIDs: %w\", err)\n\t}\n\treturn out, nil\n}\n","sourceCodeStart":952,"sourceCodeEnd":988,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/dependency.go#L952-L988","documentation":"GetDependencyRecordsForIssues calls issueops.GetDependencyRecordsForIssuesInTx to fetch dependency records grouped per issue ID. Empty input returns an empty map without touching the database, so this wrapper only fires for non-empty ID lists where the underlying batch query failed: SQL error, connection loss, or scan failure.","triggerScenarios":"Calling GetDependencyRecordsForIssues(ctx, issueIDs) with non-empty issueIDs where GetDependencyRecordsForIssuesInTx errors: dependencies table unreadable, connection dropped, context cancellation, oversized batch hitting engine limits.","commonSituations":"Rendering dependency views for many issues at once against a slow or restarted Dolt server; schema drift after upgrade; batch size exceeding IN-clause/engine limits.","solutions":["Unwrap the error for the root SQL/driver cause.","Split very large issueID lists into smaller batches before calling.","Verify connectivity and schema; retry transient failures.","Check the dependencies table integrity with doctor/repair tooling."],"exampleFix":"// before: one giant batch\nrecs, err := repo.GetDependencyRecordsForIssues(ctx, allIDs)\n// after: chunk the batch\nfor i := 0; i < len(allIDs); i += 500 {\n    end := min(i+500, len(allIDs))\n    part, err := repo.GetDependencyRecordsForIssues(ctx, allIDs[i:end])\n    if err != nil { return fmt.Errorf(\"records batch %d: %w\", i, err) }\n    merge(recs, part)\n}","handlingStrategy":"validation","validationCode":"if len(issueIDs) == 0 {\n    return map[string][]*types.Dependency{} // library short-circuits empty input\n}\nif len(issueIDs) > 1000 {\n    return errors.New(\"batch too large; split into chunks of <=1000 IDs\")\n}","typeGuard":"func isBatchable(ids []string) bool { return len(ids) > 0 && len(ids) <= 1000 }","tryCatchPattern":"recs, err := repo.GetDependencyRecordsForIssues(ctx, ids)\nif err != nil {\n    if errors.Is(errors.Unwrap(err), driver.ErrBadConn) {\n        // retry with a fresh connection/context\n        return repo.GetDependencyRecordsForIssues(freshCtx, ids)\n    }\n    return fmt.Errorf(\"dep records: %w\", err)\n}","preventionTips":["Chunk large ID lists; IN-clause batches have practical engine limits.","Pass empty slices to get the zero-cost empty map instead of filtering later.","Retry only transient wrapped causes; surface schema errors for repair."],"tags":["database","batch-query","dependencies","wrapper"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}