TryGhost/Ghost · error · Error
Failed to update email preferences
Error message
Failed to update email preferences
What it means
Thrown in api.member.updateEmailPreferences (apps/portal/src/utils/api.js:467) when PUT to /members/api/member/newsletters/ (uuid/key auth) returns non-ok. Message 'Failed to update email preferences'. Used by the unsubscribe/manage-preferences flow when the member is not signed in but has a uuid+key token.
Source
Thrown at apps/portal/src/utils/api.js:467
body.enable_comment_notifications = enableCommentNotifications;
}
if (enableUpdatesAndAnnouncements !== undefined) {
body.enable_updates_and_announcements = enableUpdatesAndAnnouncements;
}
return makeRequest({
url,
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).then(function (res) {
if (res.ok) {
return res.json();
} else {
throw new Error('Failed to update email preferences');
}
});
},
async updateEmailAddress({email}) {
const identity = await api.member.identity();
const url = endpointFor({type: 'members', resource: 'member/email'});
const body = {
email,
identity
};
return makeRequest({
url,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Verify the uuid and key in the URL are intact and unused (request a fresh magic link if expired).
- Inspect the PUT response status/body — 404 = member gone, 401 = bad token, 422 = body invalid.
- Confirm at least one enabled newsletter exists (Settings > Newsletter).
- Re-prompt the user to sign in to manage preferences instead of relying on the token.
Example fix
// before
if (res.ok) { return res.json(); } else { throw new Error('Failed to update email preferences'); }
// after
if (res.ok) { return res.json(); }
if (res.status === 401 || res.status === 404) { throw new Error('Your preferences link has expired — request a new one'); }
throw new Error(`Failed to update email preferences (${res.status})`); Defensive patterns
Strategy: validation
Validate before calling
// validate the token pair before calling
function isValidManagePrefsToken({uuid, key}) {
return typeof uuid === 'string' && typeof key === 'string' && uuid.length > 0 && key.length > 0;
} Try / catch
try {
const updated = await api.member.updateEmailPreferences({uuid, key, newsletters});
return updated;
} catch (err) {
if (/expired/.test(err.message)) { promptSignIn(); }
else { notifyError('Could not update preferences — please try again'); }
} Prevention
- Validate the uuid/key token pair before issuing the PUT.
- Prompt the user to sign in when the token is expired.
- Confirm at least one enabled newsletter exists.
- Match Portal bundle and Ghost server versions on the preferences contract.
When it happens
Trigger: Visitor opens a manage-preferences link with uuid+key; Portal PUTs the updated newsletters list; the response is 4xx/5xx — token expired/used, member not found, no newsletters configured, or the body shape is invalid.
Common situations: The uuid/key token was already rotated or expired; member was deleted; the link was opened after the token TTL elapsed; CSRF/identity check failed; Portal bundle / server version mismatch on the newsletters endpoint contract.
Related errors
- Failed to update newsletter
- Failed to update member
- Failed to fetch site data
- fallbackMessage
- Your email has failed to resubscribe, please try again
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/c6b91601127c144b.
Report an issue: GitHub.