TryGhost/Ghost · error · Error
redirectResult.error.message
Error message
redirectResult.error.message
What it means
Thrown inside checkoutPlan() after the Ghost backend successfully created a Stripe Checkout Session but Stripe.js's redirectToCheckout() rejected it. This is a Stripe.js client-side failure, not a Ghost backend failure — the sessionId/publicKey came back fine, but the browser could not hand off to Stripe's hosted checkout page.
Source
Thrown at apps/portal/src/utils/api.js:566
if (!res.ok) {
const errData = await res.json();
const err = errData?.errors?.[0] || {};
const errMssg = err.message || 'Failed to signup, please try again.';
const error = new Error(errMssg);
error.code = err.code;
throw error;
}
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);
}
});
});
},
async continueGiftCheckout() {
const siteUrlObj = new URL(siteUrl);
const identity = await api.member.identity();
const url = endpointFor({type: 'members', resource: 'create-stripe-checkout-session'});
const checkoutSuccessUrl = window.location.href.startsWith(siteUrlObj.href) ? new URL(window.location.href) : new URL(siteUrl);
checkoutSuccessUrl.searchParams.set('stripe', 'success');
const checkoutCancelUrl = window.location.href.startsWith(siteUrlObj.href) ? new URL(window.location.href) : new URL(siteUrl);
checkoutCancelUrl.searchParams.set('stripe', 'cancel');
const body = {
type: 'subscription',View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Refresh the page and restart checkout — a fresh session is created each time and bypasses a stale/expired sessionId.
- Verify Stripe publishable/secret key parity in Ghost Admin settings (Stripe Connect integration) — a test key paired with live mode is the classic cause.
- Check browser console for Stripe.js load failures (network/CSP) and ensure the Stripe.js script tag is present and unblocked.
- Check status.stripe.com for a Checkout outage.
- If reproducible, capture redirectResult.error.code/type from Stripe (e.g. 'invalid_session_id') for a precise diagnosis.
Example fix
// before: throws raw Stripe error.message with no context
stripe.redirectToCheckout({sessionId}).then(r => { if (r.error) throw new Error(r.error.message); });
// after: classify the Stripe error so the UI can offer a retry
stripe.redirectToCheckout({sessionId}).then(r => {
if (r.error) {
const e = new Error(r.error.message);
e.code = r.error.code;
e.retryable = ['expired', 'invalid_session_id'].includes(r.error.code);
throw e;
}
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Verify Stripe.js loaded and the key matches before redirecting
function stripeReady() {
return typeof window.Stripe === 'function' && !!window.Stripe._instance;
}
// Cannot validate sessionId expiry client-side; restart the flow to get a fresh one. Type guard
function isStripeRedirectError(result) {
return typeof result === 'object' && result !== null && typeof result.error === 'object' && typeof result.error.message === 'string';
} Try / catch
try {
await api.member.checkoutPlan({...});
} catch (err) {
// Stripe redirect failed — offer a retry that mints a new session
if (/session|stripe|checkout/i.test(err.message)) {
offerRetry();
} else {
showToast(err.message);
}
} Prevention
- Restart the checkout flow on any Stripe redirect failure — stale sessions are the top cause.
- Keep Stripe publishable and secret keys in the same mode (test/live) in Ghost Admin.
- Ensure the Stripe.js script tag is present and not CSP-blocked.
When it happens
Trigger: stripe.redirectToCheckout({sessionId}) returns {error} — typically because the sessionId is malformed/expired, the publishable key (publicKey) doesn't match the account that created the session, Stripe.js failed to load, or the session was already consumed/expired (sessions expire 24h after creation).
Common situations: Stripe keys mismatched between Ghost Admin settings and the live site (test vs live key); clock drift causing the session to appear expired; Stripe.js script blocked by an ad-blocker or CSP; user opened checkout, left it >24h, then clicked again reusing a cached sessionId; Stripe partial outage.
Related errors
- result.error.message
- Could not create Stripe checkout session
- redirectResult.error.message
- Failed to continue gift subscription, please try again.
- result.error.message
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/6fc1e1c466d3f42b.
Report an issue: GitHub.