{"record":{"id":"1ef10b3641d316d4","repo":"TryGhost/Ghost","slug":"result-error-message","errorCode":null,"errorMessage":"result.error.message","messagePattern":"result\\.error\\.message","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/portal/src/data-attributes.js","lineNumber":283,"sourceCode":"                    body: JSON.stringify({\n                        identity: identity,\n                        successUrl: successUrl,\n                        cancelUrl: cancelUrl\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 (result) {\n                let 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(t(result.error.message));\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        el.addEventListener('click', clickHandler);\n    });\n\n    Array.prototype.forEach.call(document.querySelectorAll('[data-members-manage-billing]'), function (el) {\n        let errorEl = el.querySelector('[data-members-error]');\n        let membersReturn = el.dataset.membersReturn;\n        let returnUrl;","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/portal/src/data-attributes.js#L265-L301","documentation":"Thrown in apps/portal/src/data-attributes.js update-billing flow when Stripe.js redirectToCheckout (used here to enter the billing portal session) resolves with result.error populated. The thrown error message is t(result.error.message), feeding the Stripe message through Portal's i18n lookup (which usually returns the original string because no key matches).","triggerScenarios":"create-stripe-update-session returned a valid sessionId/publicKey; window.Stripe(publicKey).redirectToCheckout({sessionId}) resolves with {error:{message}} — Stripe.js could not perform the redirect (session expired, key mismatch, browser blocked).","commonSituations":"Customer portal session expired before redirect completed; publishable key returned by the backend differs from the account that created the session; browser blocking third-party cookies / popups; network interruption during redirect.","solutions":["Surface result.error.message directly to the user — wrapping it in t() often returns the English original anyway.","Have the user click the edit-billing button again (the catch re-binds the handler) to mint a fresh session.","Verify the publishable key returned by the backend matches the connected Stripe account.","Ask the user to disable content blockers that interfere with js.stripe.com."],"exampleFix":"// before\nif (result.error) {\n    throw new Error(t(result.error.message));\n}\n\n// after: don't route a dynamic Stripe message through i18n key lookup\nif (result.error) {\n    throw new Error(result.error.message);\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 result = await stripe.redirectToCheckout({sessionId});\n    if (isStripeRedirectError(result)) { throw new Error(result.error.message); }\n} catch (err) {\n    showError(el, err.message);\n    el.classList.remove('loading');\n    el.addEventListener('click', clickHandler);\n}","preventionTips":["Do not pass dynamic Stripe messages through t() — they are not i18n keys.","Re-mint the billing session on each retry.","Verify the publishable key matches the connected Stripe account.","Surface redirectResult.error.message directly to the user."],"tags":["portal","stripe","billing-update","stripe-js","redirect"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}