{"record":{"id":"eb91be7728212f3b","repo":"TryGhost/Ghost","slug":"email-error","errorCode":null,"errorMessage":"email.error","messagePattern":"email\\.error","errorType":"exception","errorClass":"EmailFailedError","httpStatus":null,"severity":"error","filePath":"apps/ember-admin/app/components/editor/modals/publish-flow/complete-with-email-error.js","lineNumber":48,"sourceCode":"    *retryEmailTask() {\n        this.retryErrorMessage = null;\n\n        try {\n            let email = yield this.args.publishOptions.post.email.retry();\n\n            let pollTimeout = 0;\n            if (email && email.status !== 'submitted') {\n                while (pollTimeout < CONFIRM_EMAIL_MAX_POLL_LENGTH) {\n                    yield timeout(CONFIRM_EMAIL_POLL_LENGTH);\n                    pollTimeout += CONFIRM_EMAIL_POLL_LENGTH;\n\n                    email = yield email.reload();\n\n                    if (email.status === 'submitted') {\n                        break;\n                    }\n                    if (email.status === 'failed') {\n                        throw new EmailFailedError(email.error);\n                    }\n                }\n            }\n\n            this.args.setCompleted();\n\n            return email;\n        } catch (e) {\n            // update \"failed\" state if email fails again\n            if (e && e.name === 'EmailFailedError') {\n                this.newEmailErrorMessage = e.message;\n                return false;\n            }\n\n            if (e) {\n                let errorMessage = '';\n\n                if (isServerUnreachableError(e)) {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/ember-admin/app/components/editor/modals/publish-flow/complete-with-email-error.js#L30-L66","documentation":"retryEmailTask is an ember-concurrency task that retries sending a post email and polls the email record's status until 'submitted' or a timeout. When email.reload() returns status === 'failed', it throws EmailFailedError(email.error) where email.error is the backend-provided failure reason. The catch block checks e.name === 'EmailFailedError' and surfaces the message as newEmailErrorMessage. The error is not a thrown-to-caller error — it is caught internally and converted to a UI state.","triggerScenarios":"The email send (Mailgun/SMTP) failed server-side and the email record's status transitioned to 'failed' with an error string in email.error. The polling loop (up to CONFIRM_EMAIL_MAX_POLL_LENGTH) observes this status. Causes: Mailgun API error, SMTP connection failure, invalid recipient list, bounce/blocklist, or rate limit from the email provider. The email.error field carries the provider-specific message.","commonSituations":"A publication's Mailgun domain is not verified, causing all sends to fail. An SMTP integration (custom mailer) has wrong credentials. The recipient list contains addresses that cause a hard bounce on a previous send, triggering provider-level blocks. A large blast hits the provider rate limit and some emails fail.","solutions":["Inspect the email.error message displayed in the UI (newEmailErrorMessage) — it carries the provider's failure reason (e.g. 'Mailgun domain not verified').","Verify the email provider configuration in Ghost Labs/settings: Mailgun domain/API key or custom SMTP credentials.","Check the Mailgun/SMTP provider dashboard for the specific failure (bounces, blocks, domain verification).","If 'partially' failed (isPartialError), some recipients received the email — review the recipient list and re-send to the failed subset after fixing the provider issue."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isEmailFailedError(e: unknown): e is Error {\n  return e instanceof Error && (e as any).name === 'EmailFailedError';\n}","tryCatchPattern":"// the task already catches internally; this is the pattern it uses:\ntry {\n  yield this.args.publishOptions.post.email.retry();\n  // ... polling loop ...\n} catch (e) {\n  if (isEmailFailedError(e)) {\n    this.newEmailErrorMessage = e.message; // provider's failure reason\n    return false;\n  }\n  // other errors (network, unknown)\n}","preventionTips":["Verify Mailgun domain verification or SMTP credentials before scheduling email sends.","Monitor email.error values across failures to detect provider config drift early.","For partial failures, review the recipient list and re-send only to the failed segment after fixing the provider issue.","Use the isPartialError check (message includes 'partially') to distinguish total vs partial send failures in the UI."],"tags":["email","ember-admin","publish-flow","mailgun","smtp","polling"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}