TryGhost/Ghost · error · Error

Failed to update newsletter

Error message

Failed to update newsletter

What it means

Thrown in apps/portal/src/actions.js updateNewsletter when api.member.update({subscribed}) resolves to a falsy member. The portal action wraps it as a localized 'Your email has failed to resubscribe, please try again' popup notification in the catch branch. It indicates the members API returned no member object for the update call.

Source

Thrown at apps/portal/src/actions.js:621

        };
    } catch (e) {
        return {
            action: 'removeEmailFromSuppressionList:failed',
            popupNotification: createPopupNotification({
                type: 'removeEmailFromSuppressionList:failed',
                autoHide: true, closeable: true, state, status: 'error',
                message: t('Your email has failed to resubscribe, please try again')
            })
        };
    }
}

async function updateNewsletter({data, state, api}) {
    try {
        const {subscribed} = data;
        const member = await api.member.update({subscribed});
        if (!member) {
            throw new Error('Failed to update newsletter');
        }
        const action = 'updateNewsletter:success';
        return {
            action,
            member: member,
            popupNotification: createPopupNotification({
                type: action, autoHide: true, closeable: true, state, status: 'success',
                message: t('Email newsletter settings updated')
            })
        };
    } catch (e) {
        return {
            action: 'updateNewsletter:failed',
            popupNotification: createPopupNotification({
                type: 'updateNewsletter:failed', autoHide: true, closeable: true, state, status: 'error',
                message: t('Failed to update newsletter settings')
            })
        };

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Confirm the member is still signed in by reloading the page; if the session expired, Portal will re-prompt sign-in.
  2. Check the Network response for PUT/PATCH /members/api/member/ — verify it returns a JSON body with a member object, not an empty 200.
  3. If running a custom reverse proxy or CDN in front of Ghost, ensure it does not cache or strip POST/PUT response bodies.
  4. Update Portal to match the Ghost server version so the response envelope shape is compatible.

Example fix

// before
const member = await api.member.update({subscribed});
if (!member) {
    throw new Error('Failed to update newsletter');
}

// after: surface server status instead of swallowing it
if (!member) {
    throw new Error('Server returned no member — session may have expired. Reload and sign in again.');
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the member session is valid before allowing a subscription toggle
async function ensureMemberSession(api) {
    const identity = await api.member.identity().catch(() => null);
    if (!identity) {
        throw new Error('Session expired — reload and sign in again');
    }
}

Type guard

function isMemberResponse(value) {
    return value != null && typeof value === 'object' && typeof value.uuid === 'string';
}

Try / catch

try {
    const member = await api.member.update({subscribed});
    if (!member) { throw new Error('Server returned no member — session may have expired'); }
    return {action: 'updateNewsletter:success', member};
} catch (e) {
    return {action: 'updateNewsletter:failed', error: e};
}

Prevention

When it happens

Trigger: A member toggles newsletter subscription in Portal; api.member.update({subscribed:true|false}) succeeds at the fetch layer (no network error) but the response body parses to a falsy member (null, undefined, or empty).

Common situations: Stale session where the member cookie exists but the server no longer recognizes the member; backend version mismatch returning an envelope the client doesn't unwrap; reverse proxy stripping the response body; member was deleted between page load and toggle click.

Related errors


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