{"record":{"id":"cdac738e06030a43","repo":"TryGhost/Ghost","slug":"email-content-is-required","errorCode":null,"errorMessage":"Email content is required","messagePattern":"Email content is required","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"ghost/core/core/server/api/endpoints/utils/validators/input/automated_emails.js","lineNumber":85,"sourceCode":"        throw new ValidationError({\n            message: errorMessage\n        });\n    }\n};\n\nconst validatePreviewData = (frame) => {\n    const subject = frame.data.subject;\n    const lexical = frame.data.lexical;\n\n    if (typeof subject !== 'string' || !subject.trim()) {\n        throw new ValidationError({\n            message: tpl(messages.subjectRequired),\n            property: 'subject'\n        });\n    }\n\n    if (typeof lexical !== 'string' || !lexical.trim()) {\n        throw new ValidationError({\n            message: tpl(messages.lexicalRequired),\n            property: 'lexical'\n        });\n    }\n\n    try {\n        JSON.parse(lexical);\n    } catch (e) {\n        throw new ValidationError({\n            message: tpl(messages.invalidLexical),\n            property: 'lexical'\n        });\n    }\n};\n\nmodule.exports = {\n    async add(apiConfig, frame) {\n        await validateAutomatedEmail(frame);","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/ghost/core/core/server/api/endpoints/utils/validators/input/automated_emails.js#L67-L103","documentation":"Thrown by `validatePreviewData` when `frame.data.lexical` is not a non-empty trimmed string. The Lexical field holds the email body content; a blank/missing value means there is nothing to render, so Ghost rejects with a 422 ValidationError on the `lexical` property. Checked after the subject requirement.","triggerScenarios":"A preview/test-email request omits `lexical`, sends it as a non-string type, or sends whitespace only.","commonSituations":"Editor sent the preview with an empty Lexical document; payload key misspelled; client constructs the request before the editor content is populated.","solutions":["Include a non-empty `lexical` string (the Lexical editor JSON) in the request body.","Verify the editor state is serialized into the `lexical` field before submit.","Ensure the field name matches exactly (`lexical`)."],"exampleFix":"// before\nawait api.emailPreview.send({data: {subject: 'Hi', email: 'a@b.com'}}); // lexical missing\n\n// after\nawait api.emailPreview.send({data: {subject: 'Hi', lexical: JSON.stringify(lexicalDoc), email: 'a@b.com'}});","handlingStrategy":"validation","validationCode":"function isValidLexicalString(s: unknown): s is string {\n    return typeof s === 'string' && s.trim().length > 0;\n}\nif (!isValidLexicalString(payload.lexical)) {\n    throw new Error('lexical must be a non-empty JSON string');\n}","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n    return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Serialize the Lexical editor state into the `lexical` field before previewing.","Ensure the editor has content before allowing preview/send.","Use the exact field name `lexical`."],"tags":["api","validation","email","automated-emails","lexical","preview"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}