{"record":{"id":"55ffa956b056eccf","repo":"gastownhall/beads","slug":"db-commentsqlrepository-insertrecord-issueid-mus","errorCode":null,"errorMessage":"db: CommentSQLRepository.InsertRecord: issueID must not be empty","messagePattern":"db: CommentSQLRepository\\.InsertRecord: issueID must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/comment.go","lineNumber":134,"sourceCode":"func (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() {\n\t\tcopy.CreatedAt = time.Now().UTC()\n\t} else {\n\t\tcopy.CreatedAt = copy.CreatedAt.UTC()\n\t}","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/comment.go#L116-L152","documentation":"After dereferencing the comment, InsertRecord requires IssueID to be non-empty because every comment row is keyed to an issue (issues/issues_wisps table). An empty IssueID would never match the foreign issue, so the repository rejects it up front with this guard.","triggerScenarios":"Calling InsertRecord with a types.Comment whose IssueID field was never set, calling it with comment.IssueID == \"\" after a failed lookup, or constructing the struct with named fields and omitting IssueID.","commonSituations":"Import scripts that forgot to map the parent issue key, code paths where the issue ID comes from a config/env value that is empty, or tests constructing partial fixtures.","solutions":["Set comment.IssueID to a valid issue ID before calling InsertRecord.","Resolve the issue (e.g. via a lookup by title/prefix) and use its ID.","Skip insertion when the parent ID is empty and log/report the orphan comment instead of calling the repository."],"exampleFix":"// before\nrepo.InsertRecord(ctx, &types.Comment{Author: \"me\", Text: \"hi\"}, opts)\n// after\nrepo.InsertRecord(ctx, &types.Comment{IssueID: issue.ID, Author: \"me\", Text: \"hi\"}, opts)","handlingStrategy":"validation","validationCode":"func validateComment(c *types.Comment) error {\n    if c == nil || c.IssueID == \"\" {\n        return fmt.Errorf(\"comment requires a non-empty IssueID\")\n    }\n    return nil\n}","typeGuard":"func hasIssueID(c *types.Comment) bool { return c != nil && c.IssueID != \"\" }","tryCatchPattern":"if _, err := repo.InsertRecord(ctx, c, opts); err != nil {\n    if strings.Contains(err.Error(), \"issueID must not be empty\") {\n        // resolve the parent issue and populate IssueID, then retry\n    }\n}","preventionTips":["Always populate IssueID when constructing a types.Comment.","Add a NewComment(issueID, author, text) constructor that asserts a non-empty issue ID.","Cover the empty-ID guard in unit tests."],"tags":["go","validation","empty-value","repository"],"backgroundTag":"empty-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}