TryGhost/Ghost · error · ValidationError
Email content is required
Error message
Email content is required
What it means
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.
Source
Thrown at ghost/core/core/server/api/endpoints/utils/validators/input/automated_emails.js:85
throw new ValidationError({
message: errorMessage
});
}
};
const validatePreviewData = (frame) => {
const subject = frame.data.subject;
const lexical = frame.data.lexical;
if (typeof subject !== 'string' || !subject.trim()) {
throw new ValidationError({
message: tpl(messages.subjectRequired),
property: 'subject'
});
}
if (typeof lexical !== 'string' || !lexical.trim()) {
throw new ValidationError({
message: tpl(messages.lexicalRequired),
property: 'lexical'
});
}
try {
JSON.parse(lexical);
} catch (e) {
throw new ValidationError({
message: tpl(messages.invalidLexical),
property: 'lexical'
});
}
};
module.exports = {
async add(apiConfig, frame) {
await validateAutomatedEmail(frame);View on GitHub (pinned to 47d8b0e2ad)
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`).
Example fix
// before
await api.emailPreview.send({data: {subject: 'Hi', email: 'a@b.com'}}); // lexical missing
// after
await api.emailPreview.send({data: {subject: 'Hi', lexical: JSON.stringify(lexicalDoc), email: 'a@b.com'}}); Defensive patterns
Strategy: validation
Validate before calling
function isValidLexicalString(s: unknown): s is string {
return typeof s === 'string' && s.trim().length > 0;
}
if (!isValidLexicalString(payload.lexical)) {
throw new Error('lexical must be a non-empty JSON string');
} Type guard
function isNonEmptyString(v: unknown): v is string {
return typeof v === 'string' && v.trim().length > 0;
} Prevention
- 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`.
When it happens
Trigger: A preview/test-email request omits `lexical`, sends it as a non-string type, or sends whitespace only.
Common situations: Editor sent the preview with an empty Lexical document; payload key misspelled; client constructs the request before the editor content is populated.
Related errors
- Subject is required
- Lexical must be a valid JSON string
- Token is required
- The server did not receive a valid email
- Failed to convert HTML to Lexical
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/cdac738e06030a43.
Report an issue: GitHub.