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

  1. Inspect the preview request response in DevTools to identify which field is missing or empty.
  2. If plaintext is legitimately empty for a text-less welcome email, relax the guard to require only html and degrade gracefully on the others.
  3. Confirm admin and ghost/core versions are compatible (the automated_emails preview serializer must emit html, plaintext, and subject).
  4. Verify the automatedEmailId still maps to a real automated email and the endpoint isn't returning an empty array.
  5. 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

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


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