{"record":{"id":"cbf3993a3c418f19","repo":"TryGhost/Ghost","slug":"failed-to-continue-gift-subscription-please-try-a","errorCode":null,"errorMessage":"Failed to continue gift subscription, please try again.","messagePattern":"Failed to continue gift subscription, please try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/portal/src/utils/api.js","lineNumber":607,"sourceCode":"                metadata: {\n                    checkoutType: 'upgrade',\n                    requestSrc: 'portal',\n                    urlHistory: getUrlHistory()\n                }\n            };\n\n            return makeRequest({\n                url,\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json'\n                },\n                body: JSON.stringify(body)\n            }).then(async function (res) {\n                if (!res.ok) {\n                    const errData = await res.json();\n                    const errMssg = errData?.errors?.[0]?.message || 'Failed to continue gift subscription, please try again.';\n                    throw new Error(errMssg);\n                }\n                return res.json();\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            });\n        },\n\n        async checkoutGift({tierId, cadence, duration, email: customerEmail} = {}) {","sourceCodeStart":589,"sourceCodeEnd":625,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/portal/src/utils/api.js#L589-L625","documentation":"Thrown by continueGiftCheckout() when the POST to the members 'create-stripe-checkout-session' endpoint (with continueFromGift:true) returns non-2xx. The client prefers the backend's errors[0].message and only falls back to this generic string when no structured error body is present. continueGiftCheckout upgrades a redeemed gift subscription into a recurring paid plan.","triggerScenarios":"A recipient who redeemed a gift subscription chooses to convert/upgrade it; the backend refuses — e.g. the gift redemption no longer maps to a valid member, the target tier is retired, Stripe customer record is missing, or the identity token doesn't match the gift recipient.","commonSituations":"Gift redemption link was already used or expired; the tier referenced by the gift was deleted/archived after the gift was purchased; member signed in as a different account than the one that redeemed the gift; Stripe customer was deleted in the Stripe dashboard leaving Ghost with a dangling reference.","solutions":["Read the concrete thrown message — if it isn't the generic fallback, it carries the backend's reason (e.g. 'Tier not found').","Confirm the currently-signed-in member is the same one who redeemed the gift (identity mismatch is common after switching accounts).","Re-open the gift redemption flow from the original link to obtain a fresh continueFromGift context.","Verify the destination tier/price still exists in Ghost Admin → Settings → Membership.","Check Ghost server logs for the members stripe-checkout-session handler error."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isMembersApiErrorBody(x) {\n    return typeof x === 'object' && x !== null && Array.isArray(x.errors) && typeof x.errors[0]?.message === 'string';\n}","tryCatchPattern":"try {\n    await api.member.continueGiftCheckout();\n} catch (err) {\n    showToast(err.message);\n    // Re-open the gift redemption link if identity/tier context is stale\n}","preventionTips":["Continue the gift from the redemption link of the account that redeemed it.","Keep the destination tier active in Ghost Admin.","Treat a backend refusal as terminal for that session — re-enter via redemption."],"tags":["portal","stripe","gift-subscription","members-api","checkout"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}