{"record":{"id":"88fe6dafea1666f2","repo":"koala73/worldmonitor","slug":"dodo-api-key-missing","errorCode":"DODO_API_KEY_MISSING","errorMessage":"DODO_API_KEY_MISSING","messagePattern":"DODO_API_KEY_MISSING","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"critical","filePath":"convex/payments/billing.ts","lineNumber":53,"sourceCode":"/**\n * Returns the direct DodoPayments REST SDK client owned by this billing module.\n *\n * This client handles billing, customer portal, and subscription operations.\n * lib/dodo.ts independently owns direct REST checkout-session creation;\n * webhook verification lives in payments/webhookHandlers.ts.\n *\n * Canonical env var: DODO_API_KEY.\n */\nfunction getDodoClient(\n  options: { timeout?: number; maxRetries?: number } = {},\n): DodoPayments {\n  const apiKey = process.env.DODO_API_KEY;\n  if (!apiKey) {\n    // Structured throw (object-typed `data`) so the client receives\n    // `err.data.kind` instead of an opaque `[Request ID: X] Server Error`\n    // (Convex's HTTP runtime drops `errorData` for string-data throws).\n    // Surfaces a config drift bug at error level so on-call sees the real cause.\n    throw new ConvexError({ kind: \"DODO_API_KEY_MISSING\" });\n  }\n  const isLive = process.env.DODO_PAYMENTS_ENVIRONMENT === \"live_mode\";\n  return new DodoPayments({\n    bearerToken: apiKey,\n    ...(isLive ? {} : { environment: \"test_mode\" as const }),\n    ...options,\n  });\n}\n\nfunction compareEntitlementPlans(\n  a: { planKey: string; validUntil: number },\n  b: { planKey: string; validUntil: number },\n): number {\n  const tierDelta = getFeaturesForPlan(a.planKey).tier - getFeaturesForPlan(b.planKey).tier;\n  if (tierDelta !== 0) return tierDelta;\n  const rankDelta = (PLAN_PRECEDENCE[a.planKey] ?? 0) - (PLAN_PRECEDENCE[b.planKey] ?? 0);\n  if (rankDelta !== 0) return rankDelta;\n  return a.validUntil - b.validUntil;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/payments/billing.ts#L35-L71","documentation":"Thrown by `getDodoClient()` in convex/payments/billing.ts when the `DODO_API_KEY` environment variable is unset on the Convex deployment. It is an object-typed ConvexError (`{ kind: \"DODO_API_KEY_MISSING\" }`) deliberately so the client receives `err.data.kind` rather than an opaque server error — Convex's HTTP runtime drops `errorData` for string-data throws. The throw is also flagged at error level so on-call sees the config drift.","triggerScenarios":"Any billing action that constructs a Dodo client (checkout, customer-portal session, subscription queries) when `DODO_API_KEY` is not set in Convex environment variables. Most commonly the first such call after a fresh deploy or environment clone.","commonSituations":"New Convex deployment/prod environment where the secret was never added; the key was named differently (e.g. `DODO_API_KEY` vs `DODO_PAYMENTS_API_KEY`); secret rotation removed the old value before adding the new one; staging env missing the var.","solutions":["Set the secret on the Convex deployment: `npx convex env set DODO_API_KEY <key>` (or via the Convex dashboard).","Confirm the exact variable name matches the canonical `DODO_API_KEY`.","Redeploy/retrigger the billing action and verify a portal/checkout call succeeds.","If rotating, add the new key before removing the old to avoid a window with no key."],"exampleFix":"# before: env missing DODO_API_KEY\n\n# after\nnpx convex env set DODO_API_KEY sk_test_xxxxxxxx","handlingStrategy":"try-catch","validationCode":"// Server-side config: ensure the secret exists before deploy\n// npx convex env set DODO_API_KEY <key>","typeGuard":null,"tryCatchPattern":"try {\n  const { portal_url } = await getCustomerPortalUrl({});\n} catch (e: any) {\n  if (e?.data?.kind === \"DODO_API_KEY_MISSING\") {\n    showConfigError(\"Billing is misconfigured. Please contact support.\");\n    return;\n  }\n  throw e;\n}","preventionTips":["Add DODO_API_KEY to every Convex environment (prod, staging, preview) via `npx convex env set`.","Verify the secret name exactly matches the canonical DODO_API_KEY.","When rotating, set the new key before removing the old."],"tags":["config","convex","billing","env","dodo-payments"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}