koala73/worldmonitor · critical · ConvexError
DODO_API_KEY_MISSING
DODO_API_KEY_MISSING
Error message
DODO_API_KEY_MISSING
What it means
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.
Source
Thrown at convex/payments/billing.ts:53
/**
* Returns the direct DodoPayments REST SDK client owned by this billing module.
*
* This client handles billing, customer portal, and subscription operations.
* lib/dodo.ts independently owns direct REST checkout-session creation;
* webhook verification lives in payments/webhookHandlers.ts.
*
* Canonical env var: DODO_API_KEY.
*/
function getDodoClient(
options: { timeout?: number; maxRetries?: number } = {},
): DodoPayments {
const apiKey = process.env.DODO_API_KEY;
if (!apiKey) {
// Structured throw (object-typed `data`) so the client receives
// `err.data.kind` instead of an opaque `[Request ID: X] Server Error`
// (Convex's HTTP runtime drops `errorData` for string-data throws).
// Surfaces a config drift bug at error level so on-call sees the real cause.
throw new ConvexError({ kind: "DODO_API_KEY_MISSING" });
}
const isLive = process.env.DODO_PAYMENTS_ENVIRONMENT === "live_mode";
return new DodoPayments({
bearerToken: apiKey,
...(isLive ? {} : { environment: "test_mode" as const }),
...options,
});
}
function compareEntitlementPlans(
a: { planKey: string; validUntil: number },
b: { planKey: string; validUntil: number },
): number {
const tierDelta = getFeaturesForPlan(a.planKey).tier - getFeaturesForPlan(b.planKey).tier;
if (tierDelta !== 0) return tierDelta;
const rankDelta = (PLAN_PRECEDENCE[a.planKey] ?? 0) - (PLAN_PRECEDENCE[b.planKey] ?? 0);
if (rankDelta !== 0) return rankDelta;
return a.validUntil - b.validUntil;View on GitHub (pinned to ffec79ac33)
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.
Example fix
# before: env missing DODO_API_KEY # after npx convex env set DODO_API_KEY sk_test_xxxxxxxx
Defensive patterns
Strategy: try-catch
Validate before calling
// Server-side config: ensure the secret exists before deploy // npx convex env set DODO_API_KEY <key>
Try / catch
try {
const { portal_url } = await getCustomerPortalUrl({});
} catch (e: any) {
if (e?.data?.kind === "DODO_API_KEY_MISSING") {
showConfigError("Billing is misconfigured. Please contact support.");
return;
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- [backfillCanary250] RESEND_API_KEY not set — run with the sa
- NO_CUSTOMER
- DODO_PORTAL_ERROR
- API_ACCESS_REQUIRED
- [backfillCanary250] CANARY_SENT_AT_MS failed Date.parse — ch
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/88fe6dafea1666f2.
Report an issue: GitHub.