{"record":{"id":"e8d3562a9f4a7fc2","repo":"TryGhost/Ghost","slug":"the-server-did-not-receive-a-valid-email-e8d356","errorCode":null,"errorMessage":"The server did not receive a valid email","messagePattern":"The server did not receive a valid email","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"ghost/core/core/server/api/endpoints/utils/validators/input/automation_email_previews.js","lineNumber":51,"sourceCode":"\n    if (!await lexicalLib.validate(lexical)) {\n        throw new ValidationError({\n            message: tpl(messages.invalidLexical),\n            property: 'lexical'\n        });\n    }\n};\n\nmodule.exports = {\n    async preview(apiConfig, frame) {\n        await validatePreviewData(frame);\n    },\n\n    async sendTestEmail(apiConfig, frame) {\n        const email = frame.data.email;\n\n        if (typeof email !== 'string' || !validator.isEmail(email)) {\n            throw new ValidationError({\n                message: tpl(messages.invalidEmailReceived),\n                property: 'email'\n            });\n        }\n\n        await validatePreviewData(frame);\n    }\n};\n","sourceCodeStart":33,"sourceCodeEnd":60,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/ghost/core/core/server/api/endpoints/utils/validators/input/automation_email_previews.js#L33-L60","documentation":"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'`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait api.sendTestEmail({data: {email: rawInput, subject, lexical}});\n\n// after\nconst email = String(rawInput || '').trim();\nif (!validator.isEmail(email)) throw new Error('Enter a valid recipient email');\nawait api.sendTestEmail({data: {email, subject, lexical}});","handlingStrategy":"validation","validationCode":"const validator = require('@tryghost/validator');\nfunction validateTestRecipient(email) {\n  if (typeof email !== 'string') throw new Error('email must be a string');\n  const trimmed = email.trim();\n  if (!validator.isEmail(trimmed)) throw new Error('Email is not valid');\n  return trimmed;\n}","typeGuard":"const isValidEmail = (v) => typeof v === 'string' && validator.isEmail(v.trim());","tryCatchPattern":"try {\n  await api.sendTestEmail({data: {email, subject, lexical}});\n} catch (err) {\n  if (err.type === 'ValidationError' && err.property === 'email') showFieldError('email', err.message);\n  else throw err;\n}","preventionTips":["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."],"tags":["validation","email","automation","email-preview","api-input"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}