TryGhost/Ghost · error · Error
Could not create Stripe checkout session
Error message
Could not create Stripe checkout session
What it means
Thrown in apps/portal/src/data-attributes.js when the POST to {siteUrl}/members/api/create-stripe-checkout/ returns a non-ok HTTP status. The error message is the i18n string 'Could not create Stripe checkout session'. The promise then chain rejects, falling into the .catch that re-enables the button and shows the error.
Source
Thrown at apps/portal/src/data-attributes.js:180
return null;
}
return res.text();
}).then(function (identity) {
return fetch(`${siteUrl}/members/api/create-stripe-checkout-session/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
...requestData,
identity: identity,
successUrl: checkoutSuccessUrl,
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);View on GitHub (pinned to 47d8b0e2ad)
Solutions
- In Ghost admin go to Settings > Membership and confirm Stripe is connected and the tiers have valid prices.
- Check the response status and body in the Network tab for create-stripe-checkout — 402/403/429 each have distinct fixes.
- If 429, slow down — Portal enforces per-email and global checkout attempt limits.
- Ensure siteUrl in the Ghost config matches the actual origin so successUrl/cancelUrl validate server-side.
Example fix
// before
if (!res.ok) {
throw new Error(t('Could not create Stripe checkout session'));
}
// after: include the server reason for debugging
if (!res.ok) {
let reason = await res.text().catch(() => '');
throw new Error(t('Could not create Stripe checkout session') + (reason ? ` (${res.status}: ${reason})` : ''));
} Defensive patterns
Strategy: try-catch
Validate before calling
// confirm tier is configured for checkout before opening Portal
function assertTierReady(tier) {
if (!tier?.monthlyPrice && !tier?.yearlyPrice) {
throw new Error('Tier has no price configured');
}
} Try / catch
try {
await startCheckout(requestData);
} catch (err) {
// err.message is i18n('Could not create Stripe checkout session')
showError(el, err.message);
el.classList.remove('loading');
el.addEventListener('click', clickHandler);
} Prevention
- Verify Stripe connection and tier prices in Settings > Membership before launching checkout.
- Rate-limit checkout clicks client-side to avoid tripping server-side 429s.
- Ensure siteUrl config matches the page origin so successUrl/cancelUrl validate.
- Keep Portal bundle version aligned with the Ghost server version.
When it happens
Trigger: Visitor clicks a Portal checkout button for a tier; the create-stripe-checkout endpoint responds with 4xx/5xx (e.g. 402 TierNotConfigured, 429 rate limited, 500 Stripe key missing). res.ok is false so the throw fires.
Common situations: Stripe connect account not configured or disconnected in Ghost admin; tier has no price configured; checkout rate limit triggered by repeated attempts; member already has an active subscription; site URL mismatch breaking the successUrl/cancelUrl; Ghost server version older than Portal bundle.
Related errors
- redirectResult.error.message
- redirectResult.error.message
- Failed to continue gift subscription, please try again.
- Failed to process gift checkout, please try again.
- result.error.message
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/1ecc2c8ba65ec877.
Report an issue: GitHub.