TryGhost/Ghost · error · ValidationError

Token is required

Error message

Token is required

What it means

Thrown by the `verifySenderUpdate` validator when `frame.data.token` is not a non-empty trimmed string. The sender-update verification flow requires the token from the confirmation email; without it Ghost cannot verify the sender change and returns a 422 ValidationError.

Source

Thrown at ghost/core/core/server/api/endpoints/utils/validators/input/automated_emails.js:119

module.exports = {
    async add(apiConfig, frame) {
        await validateAutomatedEmail(frame);
    },
    async edit(apiConfig, frame) {
        await validateAutomatedEmail(frame);
    },
    editSenders(apiConfig, frame) {
        const senderName = frame.data.sender_name;
        const senderEmail = frame.data.sender_email;
        const senderReplyTo = frame.data.sender_reply_to;

        validateOptionalStringField(senderName, 'Sender name must be a string');
        validateOptionalStringField(senderEmail, 'Sender email must be a string');
        validateOptionalStringField(senderReplyTo, 'Reply-to email must be a string');
    },
    verifySenderUpdate(apiConfig, frame) {
        if (typeof frame.data.token !== 'string' || !frame.data.token.trim()) {
            throw new ValidationError({
                message: tpl(messages.tokenRequired)
            });
        }
    },
    preview(apiConfig, frame) {
        validatePreviewData(frame);
    },
    sendTestEmail(apiConfig, frame) {
        const email = frame.data.email;

        if (typeof email !== 'string' || !validator.isEmail(email)) {
            throw new ValidationError({
                message: tpl(messages.invalidEmailReceived)
            });
        }

        validatePreviewData(frame);
    }

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Extract the `token` query parameter from the verification email/link and include it in the request body.
  2. Confirm the token is a non-empty string before submitting.
  3. Re-request the sender update email if the token was lost.

Example fix

// before
await api.email.verifySenderUpdate({data: {}}); // no token

// after
await api.email.verifySenderUpdate({data: {token: urlSearchParams.get('token')}});
Defensive patterns

Strategy: validation

Validate before calling

function isNonEmptyToken(v: unknown): v is string {
    return typeof v === 'string' && v.trim().length > 0;
}
if (!isNonEmptyToken(payload.token)) {
    throw new Error('token is required');
}

Type guard

function isNonEmptyString(v: unknown): v is string {
    return typeof v === 'string' && v.trim().length > 0;
}

Prevention

When it happens

Trigger: A request to the verify-sender-update endpoint omits `token`, sends it as a non-string type, or sends whitespace only.

Common situations: User clicked a verification link whose token query param was stripped; frontend forwarded the request without extracting the token from the URL; token expired/cleared; payload key misspelled.

Related errors


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