koala73/worldmonitor · error · ConvexError
DODO_PORTAL_ERROR
DODO_PORTAL_ERROR
Error message
DODO_PORTAL_ERROR
What it means
Structured ConvexError thrown by createCustomerPortalUrlForUser when the Dodo REST SDK raises during portal-session creation. It wraps a plain SDK Error (APIError on a non-2xx Dodo response or a transport failure) because Convex's action runtime masks non-ConvexError throws as an opaque server error — this preserves the cause for the client and Sentry via err.data.kind === 'DODO_PORTAL_ERROR'.
Solutions
- Check the wrapped original error (cause / Sentry context) to distinguish a Dodo API rejection from a network failure
- Verify the Dodo customer id is valid and the DODO_API_KEY environment is healthy
- Retry once for transient transport failures; Dodo-side rejections need corrected customer data
Defensive patterns
Strategy: try-catch
When it happens
Trigger: Thrown at convex/payments/billing.ts:470 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21).
Data as JSON: /api/errors/65caa439081b49bc.
Report an issue: GitHub.
Appendix: source
Thrown at convex/payments/billing.ts:471
} catch (err) {
// The Dodo REST SDK throws a plain Error (APIError on a non-2xx Dodo
// response, or a transport failure) when the portal-session create
// fails. Convex's action runtime then masks any NON-ConvexError throw
// as an opaque `[Request ID: X] Server Error`, dropping the real cause
// from the wire — the exact opacity WORLDMONITOR-R5 fought for the
// missing-customer path above (this was the last unwrapped throw site).
// Re-throw as a structured ConvexError so the client receives
// `err.data.kind === 'DODO_PORTAL_ERROR'` for proper Sentry
// classification (browser → `extractBillingErrorKind` → tag
// `billing_error_kind`; the user still falls back to the generic Dodo
// portal), and log the underlying cause here so it survives in the
// Convex function logs for server-side triage. WORLDMONITOR-ST.
const cause = err instanceof Error ? err.message : String(err);
console.error(
`[billing] Dodo customer-portal create failed for customer ${dodoCustomerId}:`,
cause,
);
throw new ConvexError({ kind: "DODO_PORTAL_ERROR" });
}
return { portal_url: session.link };
}
function getSubscriptionStatusPriority(status: string): number {
switch (status) {
case "active":
return 0;
case "on_hold":
return 1;
case "cancelled":
return 2;
default:
return 3;
}
}
View on GitHub (pinned to 7d06c8633d)