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

  1. Retry after the `retryAfterSeconds` value carried in the error data
  2. Apply client-side backoff and avoid hammering the checkout endpoint
  3. 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

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


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/64a0be736f92d840. Report an issue: GitHub.