{"record":{"id":"6308839aa9f250a7","repo":"gastownhall/beads","slug":"journal-marshal-comment-for-s-w","errorCode":null,"errorMessage":"journal: marshal comment for %s: %w","messagePattern":"journal: marshal comment for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/journal.go","lineNumber":441,"sourceCode":"\t\tb, err := json.Marshal(issue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal issue %s: %w\", issueID, err)\n\t\t}\n\t\tissueJSON = string(b)\n\t}\n\tvar depJSON any\n\tif dep != nil {\n\t\tb, err := json.Marshal(dep)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal dep for %s: %w\", issueID, err)\n\t\t}\n\t\tdepJSON = string(b)\n\t}\n\tvar commentJSON any\n\tif comment != nil {\n\t\tb, err := json.Marshal(comment)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal comment for %s: %w\", issueID, err)\n\t\t}\n\t\tcommentJSON = string(b)\n\t}\n\tinsert := func(seq int64) error {\n\t\t_, err := tx.ExecContext(ctx, `\n\t\t\tINSERT INTO bd_events_journal (seq, ts, op, issue_id, actor, issue_json, dep_json, comment_json)\n\t\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?)\n\t\t`, seq, time.Now().UTC(), string(op), issueID, actor, issueJSON, depJSON, commentJSON)\n\t\treturn err\n\t}\n\n\tseq, err := nextEventSeq(ctx, tx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := insert(seq); err != nil {\n\t\t// A duplicate seq means the counter is BEHIND the journal — it was\n\t\t// restored, hand-edited, or copied from another workspace. Left alone","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/journal.go#L423-L459","documentation":"insertEventRow marshals the EventComment payload to JSON before insert; failure is wrapped as 'journal: marshal comment for %s'. Same mechanism as the issue/dep marshals but for comment events.","triggerScenarios":"RecordCommentEventInTx with an *EventComment whose fields (body, author, metadata) cannot be JSON-encoded — typically invalid UTF-8 or unsupported nested types in comment extensions.","commonSituations":"Importing comments from external systems with binary bodies; bots writing non-string metadata into comment payloads; encoding mismatches from external feeds.","solutions":["Sanitize the comment body to valid UTF-8 before adding","Only attach string/JSON-native values to comment metadata","Inspect the wrapped error for the exact field","Upgrade the library if stock payloads fail to marshal"],"exampleFix":"// before\nc := &ops.EventComment{Author: \"bot\", Body: string(rawBytes)} // rawBytes not UTF-8\nops.RecordCommentEventInTx(ctx, tx, id, c)\n// after\nc := &ops.EventComment{Author: \"bot\", Body: strings.ToValidUTF8(string(rawBytes), \"\\ufffd\")}\nops.RecordCommentEventInTx(ctx, tx, id, c)","handlingStrategy":"validation","validationCode":"if !utf8.ValidString(c.Body) {\n    return errors.New(\"comment body must be valid UTF-8\")\n}\nif _, err := json.Marshal(c); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := ops.RecordCommentEventInTx(ctx, tx, id, c); err != nil {\n    if strings.Contains(err.Error(), \"marshal comment\") { return fmt.Errorf(\"bad comment payload: %w\", err) }\n    return err\n}","preventionTips":["Sanitize external comment text to valid UTF-8 before adding","Only attach JSON-native values to comment fields","Fuzz-test importers with binary/malformed input"],"tags":["journal","json","serialization"],"backgroundTag":"json-marshal-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}