TryGhost/Ghost · error · Error

redirectResult.error.message

Error message

redirectResult.error.message

What it means

Thrown in apps/portal/src/data-attributes.js after Stripe.js redirectToCheckout returns a result with a populated error field. The error uses the Stripe-provided message verbatim (not localized) and is caught by the .catch handler that shows it in the error element next to the checkout button.

Source

Thrown at apps/portal/src/data-attributes.js:193

                cancelUrl: checkoutCancelUrl,
                metadata
            })
        }).then(function (res) {
            if (!res.ok) {
                throw new Error(t('Could not create Stripe checkout session'));
            }
            return res.json();
        });
    }).then(function (responseBody) {
        if (responseBody.url) {
            return window.location.assign(responseBody.url);
        }
        const stripe = window.Stripe(responseBody.publicKey);
        return stripe.redirectToCheckout({
            sessionId: responseBody.sessionId
        }).then(function (redirectResult) {
            if (redirectResult.error) {
                throw new Error(redirectResult.error.message);
            }
        });
    }).catch(function (err) {
        console.error(err);
        el.addEventListener('click', clickHandler);
        el.classList.remove('loading');
        if (errorEl) {
            errorEl.innerText = err.message;
        }
        el.classList.add('error');
    });
}

export function handleDataAttributes({siteUrl, site = {}, member, offers = [], doAction, captureException} = {}) {
    if (!siteUrl) {
        return;
    }

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Capture and surface redirectResult.error.message to the user — it already comes from Stripe and is usually actionable.
  2. Retry checkout by clicking the button again (the .catch re-binds the click handler); if it persists, the Stripe session likely expired.
  3. Verify the publishable key returned by create-stripe-checkout matches the Stripe account connected in Ghost admin.
  4. Ask the user to disable content/ad blockers for the site if redirects silently fail.

Example fix

// before
if (redirectResult.error) {
    throw new Error(redirectResult.error.message);
}

// after: keep the message but tag it so the UI can localize the wrapper
if (redirectResult.error) {
    const e = new Error(redirectResult.error.message);
    e.code = 'stripe_redirect_failed';
    throw e;
}
Defensive patterns

Strategy: try-catch

Type guard

function isStripeRedirectError(result) {
    return result?.error != null && typeof result.error.message === 'string';
}

Try / catch

try {
    const r = await stripe.redirectToCheckout({sessionId});
    if (isStripeRedirectError(r)) { throw new Error(r.error.message); }
} catch (err) {
    showError(el, err.message);
    el.classList.remove('loading');
    el.addEventListener('click', clickHandler);
}

Prevention

When it happens

Trigger: create-stripe-checkout succeeded and returned a sessionId/publicKey; window.Stripe(publicKey).redirectToCheckout({sessionId}) rejects or resolves with {error:{message}} — typical when the session expired, the key mismatches, or Stripe.js cannot redirect (popup blocked, third-party cookies disabled).

Common situations: User took too long between clicking checkout and Stripe loading (session expired on Stripe's side); Stripe publishable key mismatched the secret key used to create the session; Safari ITP blocking third-party cookies for the Stripe redirect; ad-blockers interfering with js.stripe.com.

Related errors


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