{"record":{"id":"20f738e157c3355d","repo":"TryGhost/Ghost","slug":"the-body-of-a-comment-cannot-be-empty","errorCode":null,"errorMessage":"The body of a comment cannot be empty","messagePattern":"The body of a comment cannot be empty","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"ghost/core/core/server/models/comment.js","lineNumber":160,"sourceCode":"            const html = trimParagraphs(\n                sanitizeHtml(this.get('html'), {\n                    allowedTags: ['p', 'br', 'a', 'blockquote'],\n                    allowedAttributes: {\n                        a: ['href', 'target', 'rel']\n                    },\n                    selfClosing: ['br'],\n                    // Enforce _blank and safe URLs\n                    transformTags: {\n                        a: sanitizeHtml.simpleTransform('a', {\n                            target: '_blank',\n                            rel: 'ugc noopener noreferrer nofollow'\n                        })\n                    }\n                })\n            ).trim();\n\n            if (html.length === 0) {\n                throw new ValidationError({\n                    message: tpl(messages.emptyComment)\n                });\n            }\n            this.set('html', html);\n        }\n    },\n\n    orderAttributes: function orderAttributes() {\n        let keys = ghostBookshelf.Model.prototype.orderAttributes.call(this, arguments);\n        keys.push('count__likes');\n        keys.push('count__net_score');\n        keys.push('count__reports');\n        return keys;\n    },\n\n    onCreated: function onCreated(model, options) {\n        const result = ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);\n","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/ghost/core/core/server/models/comment.js#L142-L178","documentation":"A ValidationError raised in the Comment model's `onSaving` hook after the raw HTML is passed through `sanitizeHtml` and trimmed. If the sanitized result is empty, the comment had no permitted content left after stripping disallowed tags/attributes. This prevents storing comments that consist solely of blocked markup or whitespace.","triggerScenarios":"Posting/replying a comment whose body is only whitespace, only disallowed tags (e.g. `<script>`, `<iframe>`, an unknown tag stripped by the allowlist), or markup whose allowed subset reduces to nothing (e.g. a bare `<br>` with no text). Editing an existing comment to such content also triggers it.","commonSituations":"A spam bot submits a payload of only disallowed tags; a user pastes only an image/embed that the comment sanitizer strips; a client sends an empty `<p></p>`; the allowlist is tightened in a Ghost version and previously-accepted markup now sanitizes to empty.","solutions":["Require at least one non-whitespace text character in the comment body on the client before posting.","Strip HTML server-side in your own code and reject if no visible text remains.","Review what tags the comment sanitizer currently allows and ensure the user's content uses allowed markup.","If embedding media, use the supported comment feature set rather than raw disallowed tags."],"exampleFix":"// before\nawait api.comments.add({html: '<br>'});\n\n// after\nconst body = '<p>Great post, thanks!</p>';\nif (!body.replace(/<[^>]*>/g, '').trim()) throw new Error('Comment has no text');\nawait api.comments.add({html: body});","handlingStrategy":"validation","validationCode":"function hasCommentText(html) {\n  const text = String(html || '').replace(/<[^>]*>/g, '').trim();\n  return text.length > 0;\n}\n// if (!hasCommentText(body)) reject('Comment body is empty');","typeGuard":"const hasVisibleText = (html) => /<[a-z0-9]+[^>]*>(.*?\\S.*?)<\\/[a-z0-9]+>|\\S/i.test(String(html || '')) && String(html || '').replace(/<[^>]*>/g, '').trim().length > 0;","tryCatchPattern":"try {\n  await api.comments.add({html: body});\n} catch (err) {\n  if (err.type === 'ValidationError' && /cannot be empty/i.test(err.message)) notifyUser('Write something first');\n  else throw err;\n}","preventionTips":["Require non-whitespace text in the comment input before enabling submit.","Preview-strip HTML client-side to mirror the sanitizer and reject empties.","Keep the comment feature set within the tags the sanitizer allows."],"tags":["validation","comments","sanitization","content"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}