{"record":{"id":"9b0fb06e7eb68fdb","repo":"gastownhall/beads","slug":"db-commentsqlrepository-insertrecord-comment-mus","errorCode":null,"errorMessage":"db: CommentSQLRepository.InsertRecord: comment must not be nil","messagePattern":"db: CommentSQLRepository\\.InsertRecord: comment must not be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/comment.go","lineNumber":130,"sourceCode":"\t}\n\treturn storage.NewSliceIter(bulk[issueID]), nil\n}\n\nfunc (r *commentSQLRepositoryImpl) Insert(ctx context.Context, issueID, author, text string, opts domain.CommentOpts) (*types.Comment, error) {\n\t// Live add: advance past the issue's newest comment so a burst inside one\n\t// second still reads back in write order (issueops.NextLiveCommentTime).\n\t// InsertRecord honors a supplied CreatedAt verbatim, which is what keeps\n\t// imported comments on their original timestamps.\n\tstamp, err := issueops.NextLiveCommentTime(ctx, r.runner, pickCommentTable(opts.UseWispsTable), issueID, time.Now())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.Insert: %w\", err)\n\t}\n\treturn r.InsertRecord(ctx, &types.Comment{IssueID: issueID, Author: author, Text: text, CreatedAt: stamp}, opts)\n}\n\nfunc (r *commentSQLRepositoryImpl) InsertRecord(ctx context.Context, comment *types.Comment, opts domain.CommentOpts) (*types.Comment, error) {\n\tif comment == nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.InsertRecord: comment must not be nil\")\n\t}\n\tcopy := *comment\n\tif copy.IssueID == \"\" {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.InsertRecord: issueID must not be empty\")\n\t}\n\n\tissueTable := pickIssueTable(opts.UseWispsTable)\n\tvar exists bool\n\t//nolint:gosec // G201: issueTable is one of two hardcoded constants\n\tif err := r.runner.QueryRowContext(ctx,\n\t\tfmt.Sprintf(\"SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?)\", issueTable), copy.IssueID).Scan(&exists); err != nil {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.InsertRecord: check issue existence: %w\", err)\n\t}\n\tif !exists {\n\t\treturn nil, fmt.Errorf(\"db: CommentSQLRepository.InsertRecord: issue %s not found\", copy.IssueID)\n\t}\n\n\tif copy.CreatedAt.IsZero() {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/comment.go#L112-L148","documentation":"InsertRecord validates its comment pointer before doing any SQL work. A nil *types.Comment means the caller has no record to insert, so the repository fails fast with this guard error rather than panicking on the subsequent struct dereference (copy := *comment). It is a pure programming/usage error, never a data or database problem.","triggerScenarios":"Calling CommentSQLRepository.InsertRecord(ctx, nil, opts) directly, or a wrapper that returns nil on an error path but passes the nil result straight into InsertRecord.","commonSituations":"Code that unmarshals comments from JSON where the field was absent (yielding nil), refactorings where an earlier nil-check was removed, or tests exercising the guard.","solutions":["Ensure the *types.Comment is non-nil before calling InsertRecord (allocate with &types.Comment{...}).","If wrapping Insert, build the record inside the wrapper as Insert does rather than forwarding a possibly-nil pointer.","Check any caller that does if c != nil { InsertRecord(ctx, c, ...) } — invert the early-return so nil never reaches the call."],"exampleFix":"// before\nvar c *types.Comment\nrepo.InsertRecord(ctx, c, opts) // guard error\n// after\nc := &types.Comment{IssueID: id, Author: author, Text: text}\nrepo.InsertRecord(ctx, c, opts)","handlingStrategy":"validation","validationCode":"func validateComment(c *types.Comment) error {\n    if c == nil {\n        return fmt.Errorf(\"comment must not be nil\")\n    }\n    return nil\n}","typeGuard":"func commentIsNotNil(c *types.Comment) bool { return c != nil }","tryCatchPattern":"if _, err := repo.InsertRecord(ctx, comment, opts); err != nil {\n    if strings.Contains(err.Error(), \"comment must not be nil\") {\n        // fix caller: allocate a non-nil *types.Comment before calling\n    }\n}","preventionTips":["Never pass pointer variables that may hold nil; construct with &types.Comment{...} at the call site.","Run staticcheck/golangci-lint nil-dereference checks to catch nil paths before they reach the repository.","Write a table-driven unit test covering the nil-input guard."],"tags":["go","nil-check","validation","repository"],"backgroundTag":"nil-argument-validation","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}