TryGhost/Ghost · error
Preview response was incomplete
Error message
Preview response was incomplete
What it means
Thrown by the welcome email preview hook (useWelcomeEmailPreview) after previewWelcomeEmail resolves, when the first automated_emails entry lacks any of html, plaintext, or subject. Same defensive contract as the automation preview: the Ghost Admin API for automated email previews must return all three rendered fields, and the hook refuses to render a partial preview.
Source
Thrown at apps/admin/src/settings/app/components/settings/membership/member-emails/use-welcome-email-preview.ts:104
setPreviewState({status: 'loading'});
try {
// Only the latest preview request is allowed to update preview state.
const response = await previewWelcomeEmail({
id: automatedEmailId,
subject: draft.subject,
lexical: draft.lexical
});
if (previewRequestIdRef.current !== requestId) {
return;
}
const preview = response.automated_emails?.[0];
if (!preview?.html || !preview?.plaintext || !preview?.subject) {
throw new Error('Preview response was incomplete');
}
setPreviewState({
status: 'success',
preview: {
...preview,
html: preparePreviewHtml(preview.html)
}
});
} catch (error) {
if (previewRequestIdRef.current !== requestId) {
return;
}
setPreviewState({
status: 'error',
message: getPreviewErrorMessage(error)
});View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Inspect the preview request response in DevTools to identify which field is missing or empty.
- If plaintext is legitimately empty for a text-less welcome email, relax the guard to require only html and degrade gracefully on the others.
- Confirm admin and ghost/core versions are compatible (the automated_emails preview serializer must emit html, plaintext, and subject).
- Verify the automatedEmailId still maps to a real automated email and the endpoint isn't returning an empty array.
- Reproduce with a non-empty subject and a Lexical doc containing content to rule out empty-input edge cases.
Example fix
// before
if (!preview?.html || !preview?.plaintext || !preview?.subject) {
throw new Error('Preview response was incomplete');
}
// after — require only html for the iframe; treat missing plaintext/subject as non-fatal
if (!preview?.html) {
throw new Error('Preview response did not contain rendered HTML');
} Defensive patterns
Strategy: fallback
Validate before calling
// Before calling enterPreview, ensure the welcome email draft has real content
import {getWelcomeEmailValidationErrors} from './welcome-email-validation';
const errs = getWelcomeEmailValidationErrors(draft);
if (errs.subject || errs.lexical || !draft.subject?.trim()) {
return;
} Type guard
// Narrow on the hook's previewFrameState before rendering
import type {WelcomeEmailPreviewFrameState} from './use-welcome-email-preview';
function isErrorState(state: WelcomeEmailPreviewFrameState): state is {status: 'error' | 'invalid'; message: string} {
return state.status === 'error' || state.status === 'invalid';
} Try / catch
// enterPreview already catches internally and sets status:'error'; consumers render a fallback UI
const {previewFrameState} = useWelcomeEmailPreview({...});
if (previewFrameState.status === 'success') {
return <iframe srcDoc={previewFrameState.html} />;
}
if (previewFrameState.status === 'error' || previewFrameState.status === 'invalid') {
return <PreviewError message={previewFrameState.message} />;
}
return <PreviewSkeleton />; Prevention
- Keep the admin frontend and ghost/core backend on compatible versions so the automated_emails preview serializer returns all three fields.
- Don't request a preview for an empty/invalid welcome email draft.
- Treat the hook's error state as a first-class UI branch.
When it happens
Trigger: response.automated_emails is undefined, empty, or its [0] element has an empty/missing html, plaintext, or subject. Concretely: backend omits plaintext for a welcome email with no text body; API shape mismatch; render partially failed; automated_emails array empty because automatedEmailId doesn't match a real automated email.
Common situations: Admin/frontend version skew with backend (automated_emails serializer changed); mailer rendering service unavailable so html comes back empty; empty subject or empty Lexical draft; welcome email record deleted between opening settings and previewing; membership/tier feature flags affecting automated email registration.
Related errors
- Preview response was incomplete
- Subject is required
- Email content is required
- Lexical must be a valid JSON string
- A view with this name already exists
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/6a00eb4d67cf7ed0.
Report an issue: GitHub.