{"record":{"id":"acd58bbba39957a6","repo":"koala73/worldmonitor","slug":"no-customer","errorCode":"NO_CUSTOMER","errorMessage":"NO_CUSTOMER","messagePattern":"NO_CUSTOMER","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/billing.ts","lineNumber":443,"sourceCode":" * the rawPayload and a same-user customers row still held the answer.\n */\nexport async function createCustomerPortalUrlForUser(\n  ctx: Pick<ActionCtx, \"runQuery\">,\n  userId: string,\n): Promise<{ portal_url: string }> {\n  const dodoCustomerId = await ctx.runQuery(\n    internal.payments.billing.getDodoCustomerIdForUserPortal,\n    { userId },\n  );\n\n  if (!dodoCustomerId) {\n    // User has no subscription at all, or every sub's rawPayload lacks a\n    // usable customer_id (very rare — would mean every webhook delivery\n    // for this user dropped the customer field). Throw structured so\n    // the client surfaces the existing \"contact support\" toast\n    // (object-typed `data` so `err.data.kind` survives the wire — see\n    // `api/_convex-error.js`).\n    throw new ConvexError({ kind: \"NO_CUSTOMER\" });\n  }\n\n  const client = getDodoClient();\n  let session;\n  try {\n    session = await client.customers.customerPortal.create(\n      dodoCustomerId,\n      { send_email: false },\n    );\n  } 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","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/payments/billing.ts#L425-L461","documentation":"Structured ConvexError thrown by createCustomerPortalUrlForUser when no usable DodoPayments customer id can be resolved for the signed-in user: the lookup query returned null because the user has no subscription, or every subscription's rawPayload lacks a customer_id (rare webhook data loss). The portal URL cannot be created without a customer, so the request fails closed rather than sending a broken request to Dodo.","triggerScenarios":"Thrown at convex/payments/billing.ts:442 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Confirm the user actually has (or had) a subscription in the customers/subscriptions tables","If subscriptions exist, inspect their rawPayload webhook data for a missing customer_id and repair from the Dodo dashboard","If the user has never subscribed, surface an in-app message instead of a portal error"],"exampleFix":null,"handlingStrategy":"validation","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"}