{"record":{"id":"bac5c888a373d940","repo":"gastownhall/beads","slug":"comment-counts-w","errorCode":null,"errorMessage":"comment counts: %w","messagePattern":"comment counts: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/comment.go","lineNumber":64,"sourceCode":"}\n\nvar _ CommentUseCase = (*commentUseCaseImpl)(nil)\n\nfunc (u *commentUseCaseImpl) GetCommentCounts(ctx context.Context, issueIDs []string) (map[string]int, error) {\n\treturn u.counts(ctx, issueIDs, false)\n}\n\nfunc (u *commentUseCaseImpl) GetWispCommentCounts(ctx context.Context, wispIDs []string) (map[string]int, error) {\n\treturn u.counts(ctx, wispIDs, true)\n}\n\nfunc (u *commentUseCaseImpl) counts(ctx context.Context, ids []string, useWisp bool) (map[string]int, error) {\n\tif len(ids) == 0 {\n\t\treturn map[string]int{}, nil\n\t}\n\tout, err := u.commentRepo.CountsByIssueIDs(ctx, ids, CommentOpts{UseWispsTable: useWisp})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"comment counts: %w\", err)\n\t}\n\treturn out, nil\n}\n\nfunc (u *commentUseCaseImpl) GetCommentsForIssues(ctx context.Context, issueIDs []string) (map[string][]*types.Comment, error) {\n\treturn u.list(ctx, issueIDs, false)\n}\n\nfunc (u *commentUseCaseImpl) GetCommentsForIssue(ctx context.Context, issueID string) ([]*types.Comment, error) {\n\treturn u.listOne(ctx, issueID, false)\n}\n\nfunc (u *commentUseCaseImpl) CountCommentsForIssue(ctx context.Context, issueID string) (int64, error) {\n\treturn u.countOne(ctx, issueID, false)\n}\n\nfunc (u *commentUseCaseImpl) IterCommentsForIssue(ctx context.Context, issueID string) (storage.Iter[types.Comment], error) {\n\treturn u.iterOne(ctx, issueID, false)","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/comment.go#L46-L82","documentation":"The comment use case's internal counts helper fetches per-issue comment counts from the repository via CountsByIssueIDs. When the underlying repository/storage query fails, the error is wrapped as \"comment counts: %w\" and propagated to GetCommentCounts or GetWispCommentCounts. It indicates a storage-layer failure, not a caller input problem.","triggerScenarios":"Calling GetCommentCounts or GetWispCommentCounts with a non-empty ID list while the backing store fails: database closed/unavailable, corrupt rows, context canceled or timed out mid-query, or wisp-table mode used when the wisps table is missing/unreadable.","commonSituations":"Database file locked or corrupted; queries issued with an expired/canceled context (user aborted, request timeout); schema drift where the wisps table is absent in older databases; transient Dolt/SQLite failures.","solutions":["Unwrap the inner error to find the storage-level cause (timeout, missing table, corruption).","Check that the database is accessible and not locked by another process; retry on transient failures.","If UseWispsTable mode fails, verify the wisps table exists/migrated, or retry via GetCommentCounts (non-wisp path).","Check context cancellation — ensure no upstream timeout cancels ctx before the query completes."],"exampleFix":"// before\nctx := context.Background() // or an already-canceled request ctx\ncounts, err := uc.GetWispCommentCounts(ctx, ids)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\ndefer cancel()\ncounts, err := uc.GetWispCommentCounts(ctx, ids)\nif err != nil {\n    var retryable bool\n    if errors.Is(err, context.DeadlineExceeded) { retryable = true }\n    // handle or retry\n}","handlingStrategy":"retry","validationCode":"if len(ids) == 0 {\n    return map[string]int{}, nil // skip the call entirely for empty input\n}\ncounts, err := useCase.GetCommentCounts(ctx, ids)","typeGuard":null,"tryCatchPattern":"counts, err := useCase.GetCommentCounts(ctx, ids)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n        // retry with a fresh context\n        counts, err = useCase.GetCommentCounts(context.Background(), ids)\n    }\n    return nil, err\n}","preventionTips":["Pass a context with adequate timeout; don't reuse canceled request contexts.","Check database accessibility/lock status before bulk comment queries.","Ensure schema migrations (wisps table) have run when using wisp-mode counts.","Distinguish storage errors from empty-input by short-circuiting empty ID slices."],"tags":["database","comments","storage","query"],"backgroundTag":"comment-count-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}