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

  1. Trim the recipient address and run an RFC check (e.g. `validator.isEmail`) client-side before submitting.
  2. Strip whitespace/newlines from the input: `email.trim()`.
  3. Confirm the request body actually includes `email` as a top-level data field.
  4. 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

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


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/e8d3562a9f4a7fc2. Report an issue: GitHub.