{"record":{"id":"bb3bba70615bc2e6","repo":"gastownhall/beads","slug":"db-commentsqlrepository-listbyissueids-scan-w","errorCode":null,"errorMessage":"db: CommentSQLRepository.ListByIssueIDs: scan: %w","messagePattern":"db: CommentSQLRepository\\.ListByIssueIDs: scan: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/comment.go","lineNumber":97,"sourceCode":"\t}\n\ttable := pickCommentTable(opts.UseWispsTable)\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\tq := fmt.Sprintf(`\n\t\tSELECT id, issue_id, author, text, created_at\n\t\tFROM %s\n\t\tWHERE issue_id IN (%s)\n\t\tORDER BY issue_id, created_at ASC, id ASC\n\t`, table, strings.Join(placeholders, \",\"))\n\trows, err := r.runner.QueryContext(ctx, q, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: %w\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar c types.Comment\n\t\tif err := rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: scan: %w\", err)\n\t\t}\n\t\tcc := c\n\t\tresult[c.IssueID] = append(result[c.IssueID], &cc)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.ListByIssueIDs: rows: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (r *commentSQLRepositoryImpl) IterByIssueID(ctx context.Context, issueID string, opts domain.CommentOpts) (storage.Iter[types.Comment], error) {\n\tbulk, err := r.ListByIssueIDs(ctx, []string{issueID}, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn storage.NewSliceIter(bulk[issueID]), nil\n}\n","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/comment.go#L79-L115","documentation":"ListByIssueIDs scans each row into a types.Comment (ID, IssueID, Author, Text, CreatedAt). This error wraps a rows.Scan failure decoding one comment row — a column value (including timestamps) could not be converted to the target Go types. No partial comment list is returned.","triggerScenarios":"Calling ListByIssueIDs/IterByIssueID when `created_at` holds a value that doesn't parse into the Comment's time field, `text`/`author` are NULL, or the driver's type mapping changed between versions.","commonSituations":"Rows written by older schema versions with different timestamp formats; imports inserting NULL author/text; driver upgrades altering time handling (DATETIME vs string parsing).","solutions":["Identify the failing column from the wrapped driver error and inspect the offending row's raw value.","Normalize legacy timestamp formats in `created_at` to what issueops.FormatAuxTime produces.","Replace NULL author/text with empty strings at the schema level (NOT NULL DEFAULT '').","Pin/align the SQL driver version to avoid Scan conversion changes."],"exampleFix":"// before: legacy timestamp format can't be scanned into time.Time\nrows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt)\n\n// after: migrate stored timestamps to the canonical format first\nUPDATE comments SET created_at = DATE_FORMAT(created_at, canonical_fmt) WHERE ...","handlingStrategy":"try-catch","validationCode":"// preflight: reject rows whose created_at can't parse canonically\nvar bad int\nconn.QueryRowContext(ctx,\n    \"SELECT COUNT(*) FROM comments WHERE created_at IS NULL OR LENGTH(created_at) = 0\").Scan(&bad)\nif bad > 0 { return fmt.Errorf(\"%d malformed comment timestamps\", bad) }","typeGuard":"func isCommentScanError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"ListByIssueIDs: scan:\")\n}","tryCatchPattern":"bulk, err := repo.ListByIssueIDs(ctx, ids, opts)\nif err != nil && isCommentScanError(err) {\n    // identify and normalize the bad row, then retry\n    return fmt.Errorf(\"bad comment row: %w\", err)\n}","preventionTips":["Keep timestamp format canonical via issueops.FormatAuxTime","Disallow NULL author/text at schema level","Run scan-compat tests after driver upgrades"],"tags":["database","sql","scan","go"],"backgroundTag":"sql-row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}