TryGhost/Ghost · error · BadRequestError

Cannot disconnect Stripe whilst you have active subscription

Error message

Cannot disconnect Stripe whilst you have active subscriptions.

What it means

Thrown by the `disconnectStripeConnectIntegration` Admin API endpoint when paid members still exist. Before clearing the Stripe keys, Ghost counts members with `status:paid`; if the total is non-zero it refuses to disconnect to avoid orphaning active subscriptions. It is a deliberate data-integrity guard returning a 400 BadRequestError.

Source

Thrown at ghost/core/core/server/api/endpoints/settings.js:93

            // We need to return all settings here, because we have calculated settings that might change
            const browse = await settingsBREADService.browse(frame.options.context);

            return browse;
        }
    },

    disconnectStripeConnectIntegration: {
        statusCode: 204,
        headers: {
            cacheInvalidate: false
        },
        permissions: {
            method: 'edit'
        },
        async query(frame) {
            const paidMembers = await membersService.api.memberBREADService.browse({limit: 0, filter: 'status:paid'});
            if (_.get(paidMembers, 'meta.pagination.total') !== 0) {
                throw new BadRequestError({
                    message: 'Cannot disconnect Stripe whilst you have active subscriptions.'
                });
            }

            await stripeService.disconnect();

            return models.Settings.edit([{
                key: 'stripe_connect_publishable_key',
                value: null
            }, {
                key: 'stripe_connect_secret_key',
                value: null
            }, {
                key: 'stripe_connect_livemode',
                value: null
            }, {
                key: 'stripe_connect_display_name',
                value: null

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Cancel or downgrade all paid members first (so `status:paid` count is zero), then retry disconnect.
  2. Bulk-cancel subscriptions via the Admin API before disconnecting Stripe Connect.
  3. Verify no pending Stripe webhooks will recreate paid members between cleanup and disconnect.
  4. If this is a test environment, reset member subscriptions through the appropriate teardown flow.
Defensive patterns

Strategy: validation

Validate before calling

// Before disconnecting, confirm zero paid members
const paid = await api.members.browse({limit: 0, filter: 'status:paid'});
if (paid.meta.pagination.total !== 0) {
    throw new Error(`Cannot disconnect: ${paid.meta.pagination.total} paid member(s) still active`);
}

Try / catch

try {
    await api.stripe.disconnect();
} catch (err) {
    if (/active subscriptions/i.test((err as Error).response.body.errors[0].message)) {
        // cancel subscriptions first, then retry
    }
    throw err;
}

Prevention

When it happens

Trigger: An admin calls `DELETE /ghost/api/admin/stripe/connect/` (disconnect) while at least one member has `status:paid`. The endpoint browses paid members and aborts before `stripeService.disconnect()` and the key-clearing edit.

Common situations: Trying to reconfigure Stripe without first cancelling or downgrading paid members; leftover paid members from prior testing; a subscription webhook created a paid member after the admin believed all were removed; importing members with paid status.

Related errors


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