TryGhost/Ghost · error · Error

Latest Stripe checkout session does not include a success UR

Error message

Latest Stripe checkout session does not include a success URL

What it means

Thrown in the `donateViaStripeCheckout` flow after `stripe.completeLatestDonationCheckout`. The flow reads `stripe.getCheckoutSessions().at(-1)?.response.success_url`; if no checkout session was recorded by the Stripe test service or its `response` lacks a `success_url`, the navigation target is undefined and the test aborts. It guards the subsequent `page.goto(successUrl)` from navigating to nothing.

Source

Thrown at e2e/helpers/playwright/flows/donations.ts:39

        await checkoutPage.fillEmail(opts.email);
    }

    const amount = await checkoutPage.getAmountInCents();
    const email = await checkoutPage.getEmail();

    await checkoutPage.submitPayment();
    await stripe.completeLatestDonationCheckout({
        amount,
        donationMessage: opts.donationMessage,
        email,
        name: opts.name
    });

    const latestCheckoutSession = stripe.getCheckoutSessions().at(-1);
    const successUrl = latestCheckoutSession?.response.success_url;

    if (!successUrl) {
        throw new Error('Latest Stripe checkout session does not include a success URL');
    }

    await page.goto(successUrl);
}

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Confirm the Stripe test service is connected and `stripe.getCheckoutSessions()` returns entries (log its length right after `completeLatestDonationCheckout`).
  2. Verify `checkoutPage.submitPayment()` actually completed and the fake Stripe server recorded the session before reading it.
  3. Ensure the test environment has Stripe enabled (the fixture forces per-test isolation for stripe tests).
  4. If the session exists but lacks `success_url`, inspect the session payload shape returned by the Stripe mock and update the access path.

Example fix

// before
const latestCheckoutSession = stripe.getCheckoutSessions().at(-1);
const successUrl = latestCheckoutSession?.response.success_url;
if (!successUrl) {
    throw new Error('Latest Stripe checkout session does not include a success URL');
}

// after — surface why it failed
const sessions = stripe.getCheckoutSessions();
const latest = sessions.at(-1);
if (!latest) {
    throw new Error(`No Stripe checkout sessions recorded (count=${sessions.length})`);
}
const successUrl = latest.response?.success_url;
if (!successUrl) {
    throw new Error(`Checkout session ${latest.id} has no success_url: ${JSON.stringify(latest.response)}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const sessions = stripe.getCheckoutSessions();
if (sessions.length === 0) {
    throw new Error('No Stripe checkout sessions recorded — checkout did not complete');
}
const successUrl = sessions.at(-1)!.response.success_url;

Type guard

function hasSuccessUrl(s: unknown): s is {response: {success_url: string}} {
    return typeof s === 'object' && s !== null
        && typeof (s as any).response?.success_url === 'string';
}

Prevention

When it happens

Trigger: The donation checkout flow runs, but the Stripe fake/test server captured zero checkout sessions (webhook not delivered, `submitPayment` did not trigger checkout creation), or the recorded session object has no `success_url` on its `response` payload.

Common situations: Stripe test service is not intercepting checkout creation; the FakeStripeCheckoutPage submission silently failed before a session was created; a version change in the Stripe mock changed the session response shape; running with `stripeEnabled` configuration off so no session is ever created.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/462d7565a966cbb0. Report an issue: GitHub.