TryGhost/Ghost · error · EmailFailedError
post.email.error
Error message
post.email.error
What it means
Thrown as an EmailFailedError (ghost-admin/app/errors/email-failed-error.js) when a published post's email record has status 'failed'. The publish-management retry loop reloads the post, checks post.email.status, and surfaces the underlying post.email.error to the editor. It represents a server-side email delivery failure, not a client-side validation problem.
Source
Thrown at apps/ember-admin/app/components/editor/publish-management.js:303
let pollTimeout = 0;
if (post.email && post.email.status !== 'submitted') {
while (pollTimeout < CONFIRM_EMAIL_MAX_POLL_LENGTH) {
yield timeout(CONFIRM_EMAIL_POLL_LENGTH);
pollTimeout += CONFIRM_EMAIL_POLL_LENGTH;
yield post.reload();
if (!post.isSent && !post.isPublished) {
// A post that is not published doesn't try to send or retry an email
break;
}
if (post.email.status === 'submitted') {
break;
}
if (post.email.status === 'failed') {
throw new EmailFailedError(post.email.error);
}
}
}
return true;
}
@task
*revertToDraftTask() {
try {
yield this.publishTask.perform({taskName: 'revertToDraftTask'});
const postType = capitalize(this.args.post.displayName);
this.notifications.showNotification(`${postType} reverted to a draft.`, {type: 'success'});
return true;
} catch (e) {
this.notifications.showAPIError(e);View on GitHub (pinned to 47d8b0e2ad)
Solutions
- In Ghost admin go to Settings > Email and verify the from-address and mail service connection, then re-test delivery.
- Open the post in the editor and use the retry-email action; if it fails again, inspect the exact post.email.error payload from the API response (Network tab on /admin/posts/<id>/?include=email) for the provider's reason.
- If using a custom SMTP/Mailgun setup, confirm the configured domain's SPF/DKIM/MX records and that the API key has 'send' scope.
- For rate-limit failures, wait and reduce batch size or schedule the send; check the email service provider dashboard for throttling.
Example fix
// before: status==='failed' surfaces raw provider error
throw new EmailFailedError(post.email.error);
// caller (publish-flow.js:103) already discriminates by name:
if (e?.name === 'EmailFailedError') { /* show modal with retry */ } Defensive patterns
Strategy: try-catch
Type guard
// distinguish the email-failed signal from other errors
function isEmailFailedError(e) {
return e?.name === 'EmailFailedError' || /email/i.test(e?.message || '');
} Try / catch
try {
yield this.publishTask.perform({taskName: 'sendEmailTask'});
} catch (e) {
if (e?.name === 'EmailFailedError') {
// show the email-failure modal with retry option (see publish-flow.js:103)
this.showEmailFailureModal(e);
} else {
throw e; // rethrow non-email errors
}
} Prevention
- Always check post.email.status after reload and before declaring success.
- Wrap email send in a retry-with-backoff that distinguishes transient (rate-limit) from permanent (config) failures.
- Surface post.email.error verbatim in the UI so the operator can act on the provider's reason.
- Validate mail-service credentials in Settings > Email before scheduling large broadcasts.
When it happens
Trigger: After triggering send/retry of a post email via publish-management's publishTask, the loop polls post.reload(); the returned post is isSent or isPublished AND post.email.status === 'failed'. The error message is whatever the API returned in post.email.error (e.g. Mailgun timeout, invalid recipient domain, rate limit).
Common situations: Mailgun/SES provider misconfiguration in Ghost settings, DNS/MX issues on recipient domains, hitting provider rate limits during large broadcasts, scheduled post going out while email service credentials expired, or the post was retried after a partial failure and the second attempt also failed.
Related errors
- The newsletter parameter doesn't match any active newsletter
- Request timed out, please try again.
- Please verify your email settings
- email.error
- Your email has failed to resubscribe, please try again
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/893a6de8557c71cc.
Report an issue: GitHub.