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

  1. Check the wrapped original error (cause / Sentry context) to distinguish a Dodo API rejection from a network failure
  2. Verify the Dodo customer id is valid and the DODO_API_KEY environment is healthy
  3. 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)