TryGhost/Ghost · error · Error

Failed to send magic link email

Error message

Failed to send magic link email

What it means

Thrown in api.member.sendMagicLink (apps/portal/src/utils/api.js:373) when the magic-link request returns non-ok and HumanReadableError cannot be built from the response. Message 'Failed to send magic link email'. Covers the sign-in/up email-send path.

Source

Thrown at apps/portal/src/utils/api.js:373

                body: JSON.stringify(body)
            });

            if (res.ok) {
                const contentType = (res.headers.get('content-type') || '').toLowerCase();
                if (contentType.includes('application/json')) {
                    try {
                        return await res.json();
                    } catch (e) {
                        // fall through to response used pre-OTC
                    }
                }
                return {};
            } else {
                const humanError = await HumanReadableError.fromApiResponse(res);
                if (humanError) {
                    throw humanError;
                }
                throw new Error('Failed to send magic link email');
            }
        },

        async verifyOTC({otc, otcRef, redirect, integrityToken}) {
            const url = endpointFor({type: 'members', resource: 'verify-otc'});
            const body = {
                otc,
                otcRef,
                redirect,
                integrityToken
            };

            const res = await makeRequest({
                url,
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Validate the email client-side before submitting.
  2. If rate-limited, wait before retrying; check the response status (typically 429).
  3. Confirm 'Allow free member signup' is enabled in Settings > Membership when applicable.
  4. Verify the Email service is configured and healthy in Settings > Email.

Example fix

// before
throw new Error('Failed to send magic link email');

// after
const e = new Error(`Failed to send magic link email (${res.status})`);
e.status = res.status;
throw e;
Defensive patterns

Strategy: validation

Validate before calling

// basic email format check before sending magic link
function isValidEmail(email) {
    return typeof email === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

Try / catch

try {
    await api.member.sendMagicLink({email});
    notify('Check your email for a sign-in link');
} catch (err) {
    // err.message === 'Failed to send magic link email'
    notifyError(err.message);
}

Prevention

When it happens

Trigger: Visitor submits an email for sign-in or signup; the POST to the magic-link endpoint responds 4xx/5xx without a JSON error envelope — e.g. invalid email format (client should pre-validate), rate limit, Email service down, member signup disabled.

Common situations: Free member signup disabled in settings; rate limit hit from repeated magic-link requests; Email provider misconfigured; reverse proxy returning an HTML error; Portal bundle out of sync with the backend magic-link contract.

Related errors


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