calcom/cal.diy · error · BadRequestException

Invalid stripe credentials.

Error message

Invalid stripe credentials.

What it means

Thrown by StripeService.validateStripeCredentials when the user's stripe_payment credential exists but its `invalid` flag is true. The flag is set when a prior Stripe API operation detected the credentials no longer work (e.g. revoked access), so validation short-circuits to 400 BadRequest without calling Stripe again.

Source

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

  async checkIfIndividualStripeAccountConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {
    const stripeCredentials = await this.credentialsRepository.findCredentialByTypeAndUserId(
      "stripe_payment",
      userId
    );

    return await this.validateStripeCredentials(stripeCredentials);
  }

  async validateStripeCredentials(
    credentials?: Credential | null
  ): Promise<{ status: typeof SUCCESS_STATUS }> {
    if (!credentials) {
      throw new NotFoundException("Credentials for stripe not found.");
    }

    if (credentials.invalid) {
      throw new BadRequestException("Invalid stripe credentials.");
    }

    const stripeKey = JSON.stringify(credentials.key);
    const stripeKeyObject = JSON.parse(stripeKey);

    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) {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Reconnect Stripe by running the OAuth connect flow again to replace the credentials with fresh ones (clearing the invalid flag).
  2. Confirm in the Stripe account under Authorized Applications that the Cal app is still approved.
  3. If the invalid flag was set in error, clear it after verifying the keys work via a Stripe API call.
Defensive patterns

Strategy: try-catch

Validate before calling

async function ensureValidStripeCredentials(api) {
  try {
    const { status } = await api.validateStripe();
    return status === 'success';
  } catch (e) {
    if (e.status === 400 && /Invalid stripe credentials/.test(e.message)) return false;
    throw e;
  }
}

Type guard

function isCredentialMarkedInvalid(cred: { invalid?: boolean } | null): boolean {
  return !!cred?.invalid;
}

Try / catch

try {
  await api.validateStripe();
} catch (e) {
  if (e.status === 400 && /Invalid stripe credentials/.test(e.message)) {
    // prompt user to reconnect Stripe
    await api.reconnectStripe();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validate after the credential row was marked invalid by a previous failed Stripe API call (the invalid flag was flipped to true), or after an admin manually marked it invalid.

Common situations: The user revoked access in their Stripe account. The Stripe refresh/access token expired or was invalidated. A prior API call hit a 401 from Stripe and the system marked the credential invalid. A disconnect was partial.

Related errors


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