{"record":{"id":"65caa439081b49bc","repo":"koala73/worldmonitor","slug":"dodo-portal-error","errorCode":"DODO_PORTAL_ERROR","errorMessage":"DODO_PORTAL_ERROR","messagePattern":"DODO_PORTAL_ERROR","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/billing.ts","lineNumber":471,"sourceCode":"  } catch (err) {\n    // The Dodo REST SDK throws a plain Error (APIError on a non-2xx Dodo\n    // response, or a transport failure) when the portal-session create\n    // fails. Convex's action runtime then masks any NON-ConvexError throw\n    // as an opaque `[Request ID: X] Server Error`, dropping the real cause\n    // from the wire — the exact opacity WORLDMONITOR-R5 fought for the\n    // missing-customer path above (this was the last unwrapped throw site).\n    // Re-throw as a structured ConvexError so the client receives\n    // `err.data.kind === 'DODO_PORTAL_ERROR'` for proper Sentry\n    // classification (browser → `extractBillingErrorKind` → tag\n    // `billing_error_kind`; the user still falls back to the generic Dodo\n    // portal), and log the underlying cause here so it survives in the\n    // Convex function logs for server-side triage. WORLDMONITOR-ST.\n    const cause = err instanceof Error ? err.message : String(err);\n    console.error(\n      `[billing] Dodo customer-portal create failed for customer ${dodoCustomerId}:`,\n      cause,\n    );\n    throw new ConvexError({ kind: \"DODO_PORTAL_ERROR\" });\n  }\n\n  return { portal_url: session.link };\n}\n\nfunction getSubscriptionStatusPriority(status: string): number {\n  switch (status) {\n    case \"active\":\n      return 0;\n    case \"on_hold\":\n      return 1;\n    case \"cancelled\":\n      return 2;\n    default:\n      return 3;\n  }\n}\n","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/payments/billing.ts#L453-L489","documentation":"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'.","triggerScenarios":"Thrown at convex/payments/billing.ts:470 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}