{"record":{"id":"7fad94e22366fe55","repo":"TryGhost/Ghost","slug":"redirectresult-error-message","errorCode":null,"errorMessage":"redirectResult.error.message","messagePattern":"redirectResult\\.error\\.message","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/portal/src/data-attributes.js","lineNumber":193,"sourceCode":"                cancelUrl: checkoutCancelUrl,\n                metadata\n            })\n        }).then(function (res) {\n            if (!res.ok) {\n                throw new Error(t('Could not create Stripe checkout session'));\n            }\n            return res.json();\n        });\n    }).then(function (responseBody) {\n        if (responseBody.url) {\n            return window.location.assign(responseBody.url);\n        }\n        const stripe = window.Stripe(responseBody.publicKey);\n        return stripe.redirectToCheckout({\n            sessionId: responseBody.sessionId\n        }).then(function (redirectResult) {\n            if (redirectResult.error) {\n                throw new Error(redirectResult.error.message);\n            }\n        });\n    }).catch(function (err) {\n        console.error(err);\n        el.addEventListener('click', clickHandler);\n        el.classList.remove('loading');\n        if (errorEl) {\n            errorEl.innerText = err.message;\n        }\n        el.classList.add('error');\n    });\n}\n\nexport function handleDataAttributes({siteUrl, site = {}, member, offers = [], doAction, captureException} = {}) {\n    if (!siteUrl) {\n        return;\n    }\n","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/portal/src/data-attributes.js#L175-L211","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Capture and surface redirectResult.error.message to the user — it already comes from Stripe and is usually actionable.","Retry checkout by clicking the button again (the .catch re-binds the click handler); if it persists, the Stripe session likely expired.","Verify the publishable key returned by create-stripe-checkout matches the Stripe account connected in Ghost admin.","Ask the user to disable content/ad blockers for the site if redirects silently fail."],"exampleFix":"// before\nif (redirectResult.error) {\n    throw new Error(redirectResult.error.message);\n}\n\n// after: keep the message but tag it so the UI can localize the wrapper\nif (redirectResult.error) {\n    const e = new Error(redirectResult.error.message);\n    e.code = 'stripe_redirect_failed';\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isStripeRedirectError(result) {\n    return result?.error != null && typeof result.error.message === 'string';\n}","tryCatchPattern":"try {\n    const r = await stripe.redirectToCheckout({sessionId});\n    if (isStripeRedirectError(r)) { throw new Error(r.error.message); }\n} catch (err) {\n    showError(el, err.message);\n    el.classList.remove('loading');\n    el.addEventListener('click', clickHandler);\n}","preventionTips":["Load Stripe.js synchronously before triggering the redirect to avoid session expiry while the script loads.","Confirm the publishable key returned by the backend matches the connected account.","Ask users to disable content/ad blockers if Stripe redirects silently fail.","Re-mint the checkout session on retry rather than reusing a stale sessionId."],"tags":["portal","stripe","checkout","stripe-js","redirect"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}