{"record":{"id":"8555c9d426678d74","repo":"gastownhall/beads","slug":"iter-comments-s-w","errorCode":null,"errorMessage":"iter comments %s: %w","messagePattern":"iter comments (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/workapi/detail.go","lineNumber":224,"sourceCode":"\n\tvar out []*types.IssueWithDependencyMetadata\n\tfor iter.Next(ctx) {\n\t\titem := iter.Value()\n\t\tif item == nil {\n\t\t\tcontinue\n\t\t}\n\t\tout = append(out, shallowDep(item))\n\t}\n\tif err := iter.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"iter dependents %s: %w\", id, err)\n\t}\n\treturn out, nil\n}\n\nfunc collectComments(ctx context.Context, src DetailSource, id string, isWisp bool) ([]*types.Comment, error) {\n\titer, err := src.IterComments(ctx, id, isWisp)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"iter comments %s: %w\", id, err)\n\t}\n\tdefer iter.Close() //nolint:errcheck // read-only iterator\n\n\tvar out []*types.Comment\n\tfor iter.Next(ctx) {\n\t\titem := iter.Value()\n\t\tif item == nil {\n\t\t\tcontinue\n\t\t}\n\t\t// storage.Iter may reuse the pointer across Next calls, so a value\n\t\t// we keep has to be our own copy.\n\t\tcomment := *item\n\t\tout = append(out, &comment)\n\t}\n\tif err := iter.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"iter comments %s: %w\", id, err)\n\t}\n\treturn out, nil","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/workapi/detail.go#L206-L242","documentation":"This error is returned by collectComments in internal/workapi/detail.go when the initial call to DetailSource.IterComments fails, i.e. the comments iterator could not even be created for the issue id. It is raised before any rows are read, and BuildIssueDetails propagates it when DetailOptions.IncludeComments is true. Unlike the best-effort label/count fields, the opt-in comment stream is deliberately fail-hard: the caller asked for rows and gets a truthful error rather than a silently short list.","triggerScenarios":"Calling BuildIssueDetails with IncludeComments=true where src.IterComments(ctx, id, isWisp) returns a non-nil error on creation: the underlying comment table cannot be queried (database connection failure, table missing/corrupt, query preparation failure, invalid id routing between issue and wisp tables).","commonSituations":"Running `bd show` (or the HTTP detail handler) on an issue with comments while the Dolt database is unreachable or locked; a storage migration or version mismatch leaving the comments/wisps tables unavailable; a bug in the seam implementation misrouting a wisp id so the query targets a missing table.","solutions":["Inspect the wrapped cause with errors.Is/errors.As to identify the underlying storage error","Verify the database is reachable and the comments (and wisps) tables exist (e.g. `bd doctor`)","Reconnect/reopen the store and retry","Check that the isWisp routing matches how the issue was resolved (GetIssueOrWisp)"],"exampleFix":"// before\nissue, isWisp, err := workapi.GetIssueOrWisp(ctx, src, id)\n// ...use issue.ID and a hardcoded isWisp=false for a second detail call\ndetails, err := workapi.BuildIssueDetails(ctx, src, issue, isWisp, opts)\n// after\nissue, isWisp, err := workapi.GetIssueOrWisp(ctx, src, id)\nif err != nil {\n    return err\n}\n// pass the resolved isWisp so comment queries route to the right table\ndetails, err := workapi.BuildIssueDetails(ctx, src, issue, isWisp, opts)\nif err != nil {\n    return fmt.Errorf(\"comments for %s: %w\", id, err)\n}","handlingStrategy":"try-catch","validationCode":"if issue == nil || issue.ID == \"\" {\n    return fmt.Errorf(\"cannot load comments: no issue id\")\n}\nif err := ctx.Err(); err != nil {\n    return fmt.Errorf(\"context already done: %w\", err)\n}","typeGuard":"func isIterOpenError(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"iter comments \")\n}","tryCatchPattern":"details, err := workapi.BuildIssueDetails(ctx, src, issue, isWisp, workapi.DetailOptions{IncludeComments: true})\nif err != nil {\n    if errors.Is(err, storage.ErrNotFound) {\n        return fmt.Errorf(\"issue %s vanished between resolve and comment load\", issue.ID)\n    }\n    return fmt.Errorf(\"comment stream could not start for %s: %w\", issue.ID, err)\n}","preventionTips":["Resolve the issue with GetIssueOrWisp and pass back the returned isWisp so comment queries route to the correct table","Set IncludeComments=true only when the caller actually consumes the rows; otherwise rely on CommentCount and CommentsOmitted","Verify database reachability before batch detail operations","Handle storage.ErrNotFound distinctly from infrastructure failures"],"tags":["go","storage","iterator","database"],"backgroundTag":"iterator-open-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}