calcom/cal.diy · critical · InternalServerErrorException
Failed generating a Stripe checkout session URL.
Error message
Failed generating a Stripe checkout session URL.
What it means
Thrown by StripeService.generateTeamCheckoutSession after stripe.checkout.sessions.create returns a session whose url is null/empty. Because a checkout session without a URL is unusable, the service throws 500 InternalServerError. This is a server-side failure, not a client input error - the cause is in the Stripe configuration or the session parameters.
Source
Thrown at apps/api/v2/src/modules/stripe/stripe.service.ts:208
quantity: 1,
},
],
customer_update: {
address: "auto",
},
// Disabled when testing locally as usually developer doesn't setup Tax in Stripe Test mode
automatic_tax: {
enabled: this.environment === "production",
},
metadata: {
pendingPaymentTeamId,
ownerId,
dubCustomerId: ownerId, // pass the userId during checkout creation for sales conversion tracking: https://d.to/conversions/stripe
},
});
if (!session.url) {
throw new InternalServerErrorException({
message: "Failed generating a Stripe checkout session URL.",
});
}
return session;
}
async getStripeCustomerIdFromUserId(userId: number) {
const user = await this.usersRepository.findById(userId);
if (!user?.email) return null;
const customerId = await this.getStripeCustomerId(user);
if (!customerId) {
return this.createStripeCustomerId(user);
}
return customerId;
}View on GitHub (pinned to 176037d0af)
Solutions
- Verify teamMonthlyPriceId is set to an active, non-archived Stripe price in the current environment.
- Confirm in the Stripe dashboard that Checkout sessions can be created on the connected account.
- Retry after fixing config; if it persists, capture the full Stripe session response in logs to see why url is null.
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the configured team price exists in Stripe before checkout.
async function teamPriceIsLive(stripe, priceId: string) {
const price = await stripe.prices.retrieve(priceId);
return price.active;
} Type guard
function hasCheckoutUrl(session: { url?: string | null }): session is { url: string } {
return typeof session.url === 'string' && session.url.length > 0;
} Try / catch
try {
const session = await stripeService.generateTeamCheckoutSession(teamId, ownerId);
return session;
} catch (e) {
if (e.status === 500 && /checkout session URL/.test(e.message ?? '')) {
// alert ops: teamMonthlyPriceId likely invalid/archived; verify config
throw new OperationalError('Stripe checkout config issue');
}
throw e;
} Prevention
- Verify teamMonthlyPriceId points to an active, non-archived Stripe price in each environment.
- Add a startup check that retrieves the price from Stripe and fails fast if it is missing/inactive.
- Alert on this 500 specifically - it indicates a config defect, not a user error.
When it happens
Trigger: Creating a team checkout session where Stripe returns a session object but no url - typically because teamMonthlyPriceId is invalid/missing, the price is archived, the Stripe account cannot create sessions, or a region/currency restriction applies.
Common situations: The STRIPE_TEAM_MONTHLY_PRICE_ID (teamMonthlyPriceId) config points to a deleted/archived Stripe price. The Stripe account is restricted from creating Checkout sessions. A Stripe-side incident. The webhook/checkout feature is disabled on the account.
Related errors
- Failed to create a customer on Stripe.
- Stripe app not found
- Missing `state` query param
- error=${error}&error_description=${error_description}
- Invalid Access token.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/6f1f864004e73051.
Report an issue: GitHub.