calcom/cal.diy · error · BadRequestException

Failed to create a customer on Stripe.

Error message

Failed to create a customer on Stripe.

What it means

Thrown by StripeService.generateTeamCheckoutSession when getStripeCustomerIdFromUserId returns null for the team owner. That helper returns null if the user has no email or if no Stripe customer id can be obtained; the session cannot be created without a customer, so the request is rejected as 400 BadRequest.

Source

Thrown at apps/api/v2/src/modules/stripe/stripe.service.ts:176

    const stripeAccount = await stripeInstance.accounts.retrieve(stripeKeyObject?.stripe_user_id);

    // both of these should be true for an account to be fully active
    if (!stripeAccount.payouts_enabled || !stripeAccount.charges_enabled) {
      throw new BadRequestException("Stripe account is not an active account");
    }

    return {
      status: SUCCESS_STATUS,
    };
  }

  async generateTeamCheckoutSession(pendingPaymentTeamId: number, ownerId: number) {
    const stripe = this.getStripe();
    const customer = await this.getStripeCustomerIdFromUserId(ownerId);

    if (!customer) {
      throw new BadRequestException("Failed to create a customer on Stripe.");
    }

    const session = await stripe.checkout.sessions.create({
      customer,
      mode: "subscription",
      allow_promotion_codes: true,
      success_url: `${this.webAppUrl}/api/teams/api/create?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${this.webAppUrl}/settings/my-account/profile`,
      line_items: [
        {
          /** We only need to set the base price and we can upsell it directly on Stripe's checkout  */
          price: this.teamMonthlyPriceId,
          /**Initially it will be just the team owner */
          quantity: 1,
        },
      ],
      customer_update: {
        address: "auto",

View on GitHub (pinned to 176037d0af)

Solutions

  1. Ensure the team owner has a valid email on their user profile before starting checkout.
  2. Verify Stripe keys are configured and reachable so customer creation succeeds.
  3. Retry checkout; if it persists, inspect server logs for the upstream customer-creation failure.
Defensive patterns

Strategy: try-catch

Validate before calling

async function ensureOwnerReadyForCheckout(api, ownerId: number) {
  const user = await api.getUser(ownerId);
  if (!user?.email) throw new Error('Team owner needs a verified email before checkout');
}

Type guard

function ownerHasEmail(user: { email?: string | null } | null): boolean {
  return !!user?.email;
}

Try / catch

try {
  const { url } = await api.generateTeamCheckoutSession(teamId, ownerId);
  window.location.href = url;
} catch (e) {
  if (e.status === 400 && /Failed to create a customer/.test(e.message)) {
    surfaceToUser('We could not start checkout. Confirm your email and try again.');
  } else throw e;
}

Prevention

When it happens

Trigger: Initiating a team checkout (e.g. upgrading/creating a team) for an owner whose user record has no email, or for whom Stripe customer creation/lookup failed upstream, so getStripeCustomerIdFromUserId resolves to null.

Common situations: The owner's account has no verified email. Stripe customer creation failed earlier due to bad keys or a network error and the null propagated. The user was created via an SSO/invite flow that left email blank. Stripe is misconfigured in this environment.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/d447b9b89a0df856. Report an issue: GitHub.