koala73/worldmonitor · error · Error
Dodo checkout session ${session.session_id} has no checkout_
Error message
Dodo checkout session ${session.session_id} has no checkout_url What it means
Thrown by `createDodoCheckoutSession` after the DodoPayments SDK's `checkoutSessions.create()` returns a session object whose `checkout_url` is null/empty. The session was created server-side (so the API call succeeded) but no redirect URL came back — surfacing it as a hard (non-429) Error rather than returning a dead link to the user. This is a plain `Error` (NOT a ConvexError) — it propagates through the action layer and is classified by `payments/checkoutRateLimit.ts`. Note the client is built with `maxRetries: 0` and a 3.5s per-attempt timeout so the bounded retry ladder owns all retry policy.
Source
Thrown at convex/lib/dodo.ts:86
maxRetries: 0,
timeout: CHECKOUT_PROVIDER_ATTEMPT_TIMEOUT_MS,
};
}
/**
* Create one checkout session — exactly one HTTP request (no SDK-internal
* retries). Throws the SDK's typed APIError on failure (status 429 for rate
* limits, classified by payments/checkoutRateLimit.ts).
*/
export async function createDodoCheckoutSession(
payload: CheckoutSessionPayload,
): Promise<CheckoutSessionResult> {
const client = new DodoPayments(buildCheckoutClientOptions(process.env));
const session = await client.checkoutSessions.create(payload);
if (!session.checkout_url) {
// Session created but no redirect URL — surface as a hard (non-429)
// failure on the existing error channel rather than returning a dead link.
throw new Error(
`Dodo checkout session ${session.session_id} has no checkout_url`,
);
}
return { checkout_url: session.checkout_url };
}
View on GitHub (pinned to ffec79ac33)
Solutions
- Log `session.session_id` and the full session object to diagnose which field Dodo omitted.
- Verify `DODO_PAYMENTS_ENVIRONMENT` matches the key's mode (test_mode vs live_mode) in the Convex dashboard.
- Check the `payload` passed to `createDodoCheckoutSession` against the Dodo API spec — required fields for URL generation must be present.
- Retry the checkout once at the action layer only if transient; do NOT retry on a deterministic provider bug — surface a user-facing error and alert ops.
- If this is a known provider regression, pin a Dodo SDK version that returns the URL reliably.
Example fix
// before — no visibility into the malformed session
const { checkout_url } = await createDodoCheckoutSession(payload);
// after — catch and surface a clear error with the session id
try {
const { checkout_url } = await createDodoCheckoutSession(payload);
window.location.href = checkout_url;
} catch (err) {
if (err.message?.includes("no checkout_url")) {
console.error("Dodo returned session without checkout_url", err.message);
showUserError("Checkout is temporarily unavailable. Please try again.");
} else throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify env and payload shape before creating a session
if (!process.env.DODO_API_KEY) throw new Error("DODO_API_KEY not set");
if (!payload.product_id || !payload.amount) throw new Error("payload missing required fields"); Type guard
function hasCheckoutUrl(session: unknown): session is { checkout_url: string } {
return typeof (session as any)?.checkout_url === "string" && (session as any).checkout_url.length > 0;
} Try / catch
try {
const { checkout_url } = await createDodoCheckoutSession(payload);
window.location.href = checkout_url;
} catch (err) {
if (err.message?.includes("no checkout_url")) {
console.error("Dodo session malformed", err.message);
showUserError("Checkout is temporarily unavailable.");
} else throw err;
} Prevention
- Verify DODO_API_KEY and DODO_PAYMENTS_ENVIRONMENT mode match in the Convex dashboard.
- Check the payload against the Dodo API spec for all URL-generating fields.
- Log session.session_id on failure for provider-side diagnosis.
- This is a plain Error (not ConvexError) — it flows through the action retry ladder; don't retry a deterministic provider bug.
- Pin a Dodo SDK version known to return checkout_url.
When it happens
Trigger: DodoPayments API returns 200 with a session that lacks `checkout_url` (provider bug, partial response, or a test-mode quirk); the `DODO_PAYMENTS_ENVIRONMENT` is misconfigured so the session is created in the wrong mode; a payload field that Dodo requires for URL generation was omitted. Distinct from a 429 (rate limit) which throws the SDK's `APIError` and is retried.
Common situations: Provider outage returning malformed responses; test_mode vs live_mode mismatch; a Dodo API version change that renames or omits the field; a payload missing a required product/amount field so Dodo creates a session shell without a checkout URL.
Related errors
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/8ee366c78c520a8e.
Report an issue: GitHub.