TryGhost/Ghost · error · ValidationError
The server did not receive a valid email
Error message
The server did not receive a valid email
What it means
A ValidationError thrown by the `sendTestEmail` automation validator when `frame.data.email` is missing, not a string, or fails `validator.isEmail()`. It is checked before the preview-data validation, so an invalid recipient blocks everything. The message ('The server did not receive a valid email') is generic; the `property` is set to `'email'`.
Source
Thrown at ghost/core/core/server/api/endpoints/utils/validators/input/automation_email_previews.js:51
if (!await lexicalLib.validate(lexical)) {
throw new ValidationError({
message: tpl(messages.invalidLexical),
property: 'lexical'
});
}
};
module.exports = {
async preview(apiConfig, frame) {
await validatePreviewData(frame);
},
async sendTestEmail(apiConfig, frame) {
const email = frame.data.email;
if (typeof email !== 'string' || !validator.isEmail(email)) {
throw new ValidationError({
message: tpl(messages.invalidEmailReceived),
property: 'email'
});
}
await validatePreviewData(frame);
}
};
View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Trim the recipient address and run an RFC check (e.g. `validator.isEmail`) client-side before submitting.
- Strip whitespace/newlines from the input: `email.trim()`.
- Confirm the request body actually includes `email` as a top-level data field.
- If the member's stored email is invalid, prompt the user to correct it before sending the test.
Example fix
// before
await api.sendTestEmail({data: {email: rawInput, subject, lexical}});
// after
const email = String(rawInput || '').trim();
if (!validator.isEmail(email)) throw new Error('Enter a valid recipient email');
await api.sendTestEmail({data: {email, subject, lexical}}); Defensive patterns
Strategy: validation
Validate before calling
const validator = require('@tryghost/validator');
function validateTestRecipient(email) {
if (typeof email !== 'string') throw new Error('email must be a string');
const trimmed = email.trim();
if (!validator.isEmail(trimmed)) throw new Error('Email is not valid');
return trimmed;
} Type guard
const isValidEmail = (v) => typeof v === 'string' && validator.isEmail(v.trim());
Try / catch
try {
await api.sendTestEmail({data: {email, subject, lexical}});
} catch (err) {
if (err.type === 'ValidationError' && err.property === 'email') showFieldError('email', err.message);
else throw err;
} Prevention
- Run the same isEmail check and trim the input client-side.
- Strip trailing newlines from pasted addresses.
- Send the recipient under the `email` data field, not a query param.
When it happens
Trigger: POSTing to send-test-email with no `email` field, a malformed address (`'foo'`, `'foo@bar'`), or `email` as a non-string. Also when the address contains trailing whitespace/newlines that fail the RFC check.
Common situations: User typos a test recipient address; a frontend omits the recipient field; the email is copied with a trailing newline; an automation passes a member's stale/invalid email; the field is sent as an array or object instead of a string.
Related errors
- Subject is required
- Email content is required
- Lexical must be a well-formed Lexical document
- Subject is required
- Email content is required
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/e8d3562a9f4a7fc2.
Report an issue: GitHub.