koala73/worldmonitor · error · ConvexError
Checkout failed: ${msg}
Error message
Checkout failed: ${msg} What it means
Catch-all in `_createCheckoutSession`: any error thrown by the Dodo Payments provider call (after bounded rate-limit retry) is logged server-side as `[checkout] createCheckout failed` and re-thrown as a ConvexError with the underlying message interpolated. This preserves the public action's contract of rejecting provider failures via the error channel.
Source
Thrown at convex/payments/checkout.ts:330
`[checkout] Dodo 429 for user=${user.userId} product=${args.productId}; retrying in ${delayMs}ms`,
),
},
);
if (isCheckoutRateLimitedOutcome(result)) {
console.warn(
`[checkout] Dodo rate limited checkout creation for user=${user.userId} product=${args.productId} after bounded retry (<=${CHECKOUT_RATE_LIMIT_MAX_ATTEMPTS} attempts); retry after ${result.retryAfterSeconds}s`,
);
return result;
}
return anonymousClaimToken
? { ...result, anonymous_claim_token: anonymousClaimToken }
: result;
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error(
`[checkout] createCheckout failed for user=${user.userId} product=${args.productId}: ${msg}`,
);
throw new ConvexError(`Checkout failed: ${msg}`);
}
}
// ---------------------------------------------------------------------------
// Public action: authenticated via Convex/Clerk auth
// ---------------------------------------------------------------------------
export const createCheckout = action({
args: {
productId: v.string(),
returnUrl: v.optional(v.string()),
discountCode: v.optional(v.string()),
referralCode: v.optional(v.string()),
// "Start a new checkout anyway" — skips ONLY the pending-payment guard
// (#4438). The subscription guard still applies.
bypassPendingGuard: v.optional(v.boolean()),
},
handler: async (ctx, args) => {View on GitHub (pinned to ffec79ac33)
Solutions
- Inspect the interpolated `msg` and the server log line `[checkout] createCheckout failed for user=... product=...: <msg>` for the root cause
- Verify the productId resolves via resolveProductToPlan in the product catalog
- Validate discountCode/referralCode before submitting if applicable
- Retry once if the msg indicates a transient network/provider error; escalate if persistent
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate product/discount inputs before checkout to reduce provider failures.
const product = resolveProductToPlan(args.productId);
if (!product) { /* block: unknown productId */ } Try / catch
try {
const result = await convex.action(api.payments.checkout.createCheckout, args);
} catch (err) {
// err.message starts with 'Checkout failed: ' + provider detail
// inspect the server log [checkout] createCheckout failed for user=... product=...
if (/rate/i.test(err.message)) { /* back off */ } else { /* surface detail */ }
} Prevention
- Validate productId against the catalog client-side before checkout
- Surface the interpolated provider message to aid diagnosis
- Retry only when the message indicates a transient error
When it happens
Trigger: The Dodo checkout-creation call raises: invalid productId, expired/invalid discount or referral code, Dodo API error response, network/transport failure, or a non-Error exception. The interpolated `msg` carries the specifics.
Common situations: Invalid productId not in the catalog; expired discount code; Dodo provider outage; malformed customer data; rate-limit retries already exhausted and re-surfaced here.
Related errors
- Dodo checkout session ${session.session_id} has no checkout_
- CHECKOUT_RATE_LIMITED
- Invalid returnUrl: must be a valid absolute URL
- Invalid returnUrl: must use a trusted worldmonitor.app origi
- ACTIVE_SUBSCRIPTION_EXISTS
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/bc717efe0c15b856.
Report an issue: GitHub.