koala73/worldmonitor · warning · ConvexError
CHECKOUT_RATE_LIMITED
CHECKOUT_RATE_LIMITED
Error message
Checkout is temporarily rate limited. Retry shortly.
What it means
After bounded retry (`CHECKOUT_RATE_LIMIT_MAX_ATTEMPTS`), if Dodo still rate-limits checkout creation, the typed rate-limited outcome is converted into a ConvexError carrying `CHECKOUT_RATE_LIMITED`, a message, and `retryAfterSeconds`. The public action rejects here (the internal relay instead translates the same outcome into HTTP 429 + Retry-After for the trusted gateway).
Source
Thrown at convex/payments/checkout.ts:388
if (pending) {
throw new ConvexError(buildPendingBlockedPayload(pending));
}
const customerName = identity
? [identity.givenName, identity.familyName].filter(Boolean).join(" ") ||
identity.name
: undefined;
const result = await _createCheckoutSession(args, {
userId,
email: identity?.email,
name: customerName,
});
// The public Convex action historically rejects provider failures. Keep
// that error-channel contract: only the trusted internal relay consumes
// the typed outcome and translates it into HTTP 429 + Retry-After.
if (isCheckoutRateLimitedOutcome(result)) {
throw new ConvexError({
code: CHECKOUT_RATE_LIMITED,
message: "Checkout is temporarily rate limited. Retry shortly.",
retryAfterSeconds: result.retryAfterSeconds,
});
}
return result;
},
});
// ---------------------------------------------------------------------------
// Internal action: called by /relay/create-checkout with trusted userId
// ---------------------------------------------------------------------------
export const internalCreateCheckout = internalAction({
args: {
userId: v.string(),
email: v.optional(v.string()),
name: v.optional(v.string()),View on GitHub (pinned to ffec79ac33)
Solutions
- Retry after the `retryAfterSeconds` value carried in the error data
- Apply client-side backoff and avoid hammering the checkout endpoint
- If the rate limit persists well beyond retryAfter, check Dodo status or contact support
Defensive patterns
Strategy: retry
Try / catch
try {
await convex.action(api.payments.checkout.createCheckout, args);
} catch (err) {
if (err.data?.code === 'CHECKOUT_RATE_LIMITED') {
const secs = err.data.retryAfterSeconds ?? 30;
// schedule retry after secs; surface 'temporarily rate limited' to user
} else { throw err; }
} Prevention
- Honor retryAfterSeconds from the error data
- Apply client-side backoff to avoid retry storms
- Debounce repeated checkout button clicks
When it happens
Trigger: Dodo returns 429 on checkout creation and the bounded retry loop exhausts its attempts; the result is detected via `isCheckoutRateLimitedOutcome`.
Common situations: Burst of checkout attempts from one user/IP; Dodo-side throttling; retry storm from clients that ignore Retry-After.
Related errors
- Dodo checkout session ${session.session_id} has no checkout_
- Checkout failed: ${msg}
- 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/64a0be736f92d840.
Report an issue: GitHub.