{"record":{"id":"1495f861b05967a6","repo":"TryGhost/Ghost","slug":"result-error-message-1495f8","errorCode":null,"errorMessage":"result.error.message","messagePattern":"result\\.error\\.message","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/portal/src/utils/api.js","lineNumber":768,"sourceCode":"                body: JSON.stringify({\n                    identity: identity,\n                    subscription_id: subscriptionId,\n                    successUrl,\n                    cancelUrl\n                })\n            }).then(function (res) {\n                if (!res.ok) {\n                    throw new Error('Unable to create stripe checkout session');\n                }\n                return res.json();\n            }).then(function (result) {\n                const stripe = window.Stripe(result.publicKey);\n                return stripe.redirectToCheckout({\n                    sessionId: result.sessionId\n                });\n            }).then(function (result) {\n                if (result.error) {\n                    throw new Error(result.error.message);\n                }\n            }).catch(function (err) {\n                throw err;\n            });\n        },\n\n        async manageBilling({returnUrl, subscriptionId} = {}) {\n            const identity = await api.member.identity();\n            const url = endpointFor({type: 'members', resource: 'create-stripe-billing-portal-session'});\n            if (!returnUrl) {\n                const returnUrlObj = new URL(siteUrl);\n                returnUrlObj.searchParams.set('stripe', 'billing-portal-closed');\n                returnUrl = returnUrlObj.href;\n            }\n\n            return makeRequest({\n                url,\n                method: 'POST',","sourceCodeStart":750,"sourceCodeEnd":786,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/portal/src/utils/api.js#L750-L786","documentation":"Thrown inside editBilling() after the backend returned a valid update session but Stripe.js redirectToCheckout() failed. Same Stripe-redirect failure class as errors 81/83, scoped to the card-update (billing edit) flow. The session was created; the browser-side handoff to Stripe's hosted checkout broke.","triggerScenarios":"stripe.redirectToCheckout({sessionId}) resolves with {error} for the billing-update session — expired sessionId, publishable key mismatch with the session's account, Stripe.js blocked, or the session already consumed.","commonSituations":"Member opened the edit-billing flow, left it idle past the session expiry, then clicked through; Stripe keys changed in Ghost Admin between session creation and redirect; Stripe.js blocked by CSP/ad-blocker; Stripe partial outage on Checkout.","solutions":["Close and reopen the edit-billing flow to mint a fresh session.","Verify Stripe publishable key parity in Ghost Admin settings.","Check browser console for the Stripe.js error code/type.","Consult status.stripe.com for a Checkout incident."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"function stripeReady() {\n    return typeof window.Stripe === 'function';\n}","typeGuard":"function isStripeRedirectError(result) {\n    return result && typeof result.error === 'object' && typeof result.error.message === 'string';\n}","tryCatchPattern":"try {\n    await api.member.editBilling({subscriptionId});\n} catch (err) {\n    if (/session|stripe/i.test(err.message)) {\n        // re-enter edit billing to mint a fresh session\n    }\n    showToast(err.message);\n}","preventionTips":["Reopen the edit-billing flow to get a fresh Stripe session.","Keep Stripe keys consistent; block-check js.stripe.com reachability.","Treat Stripe redirect errors as retryable."],"tags":["portal","stripe","stripe-redirect","billing","client-side"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}