{"record":{"id":"aef0ea57db5fe8b4","repo":"calcom/cal.diy","slug":"invalid-stripe-credentials","errorCode":null,"errorMessage":"Invalid stripe credentials.","messagePattern":"Invalid stripe credentials\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/stripe/stripe.service.ts","lineNumber":153,"sourceCode":"\n  async checkIfIndividualStripeAccountConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {\n    const stripeCredentials = await this.credentialsRepository.findCredentialByTypeAndUserId(\n      \"stripe_payment\",\n      userId\n    );\n\n    return await this.validateStripeCredentials(stripeCredentials);\n  }\n\n  async validateStripeCredentials(\n    credentials?: Credential | null\n  ): Promise<{ status: typeof SUCCESS_STATUS }> {\n    if (!credentials) {\n      throw new NotFoundException(\"Credentials for stripe not found.\");\n    }\n\n    if (credentials.invalid) {\n      throw new BadRequestException(\"Invalid stripe credentials.\");\n    }\n\n    const stripeKey = JSON.stringify(credentials.key);\n    const stripeKeyObject = JSON.parse(stripeKey);\n\n    const stripeAccount = await stripeInstance.accounts.retrieve(stripeKeyObject?.stripe_user_id);\n\n    // both of these should be true for an account to be fully active\n    if (!stripeAccount.payouts_enabled || !stripeAccount.charges_enabled) {\n      throw new BadRequestException(\"Stripe account is not an active account\");\n    }\n\n    return {\n      status: SUCCESS_STATUS,\n    };\n  }\n\n  async generateTeamCheckoutSession(pendingPaymentTeamId: number, ownerId: number) {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/stripe/stripe.service.ts#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reconnect Stripe by running the OAuth connect flow again to replace the credentials with fresh ones (clearing the invalid flag).","Confirm in the Stripe account under Authorized Applications that the Cal app is still approved.","If the invalid flag was set in error, clear it after verifying the keys work via a Stripe API call."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"async function ensureValidStripeCredentials(api) {\n  try {\n    const { status } = await api.validateStripe();\n    return status === 'success';\n  } catch (e) {\n    if (e.status === 400 && /Invalid stripe credentials/.test(e.message)) return false;\n    throw e;\n  }\n}","typeGuard":"function isCredentialMarkedInvalid(cred: { invalid?: boolean } | null): boolean {\n  return !!cred?.invalid;\n}","tryCatchPattern":"try {\n  await api.validateStripe();\n} catch (e) {\n  if (e.status === 400 && /Invalid stripe credentials/.test(e.message)) {\n    // prompt user to reconnect Stripe\n    await api.reconnectStripe();\n  } else throw e;\n}","preventionTips":["Surface an 'invalid credentials - reconnect' prompt rather than a generic error when this fires.","Periodically validate credentials and proactively prompt reconnection if Stripe revokes access.","After any Stripe 401 during normal operation, mark credentials invalid and revalidate."],"tags":["stripe","credentials","api-v2","revoked"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}