{"record":{"id":"ebc71f406b78d6e8","repo":"koala73/worldmonitor","slug":"createprolaunchbroadcast-resend-api-key-not-set","errorCode":null,"errorMessage":"[createProLaunchBroadcast] RESEND_API_KEY not set","messagePattern":"\\[createProLaunchBroadcast\\] RESEND_API_KEY not set","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/sendBroadcast.ts","lineNumber":68,"sourceCode":"/**\n * Create a Resend Broadcast from the locked launch content. Does NOT\n * send — separate `sendProLaunchBroadcast` step. Returns the new\n * broadcast id so the operator can preview in the Resend dashboard\n * before firing.\n *\n * Idempotency: NOT idempotent on segmentId. Calling twice with the same\n * segment creates two separate broadcasts. The operator owns \"have I\n * already created this?\" tracking.\n */\nexport const createProLaunchBroadcast = internalAction({\n  args: {\n    segmentId: v.string(),\n    nameSuffix: v.optional(v.string()),\n  },\n  handler: async (_ctx, { segmentId, nameSuffix }) => {\n    const apiKey = process.env.RESEND_API_KEY;\n    if (!apiKey) {\n      throw new Error(\"[createProLaunchBroadcast] RESEND_API_KEY not set\");\n    }\n\n    // Hard-gate: refuse to ship the placeholder physical address.\n    // CAN-SPAM requires a valid postal address in every commercial\n    // email footer; sending the literal \"physical address TBD\" string\n    // is non-compliant and embarrassing. Edit\n    // `PRO_LAUNCH_PHYSICAL_ADDRESS` in proLaunchEmailContent.ts to the\n    // real postal address before invoking this action.\n    if (\n      PRO_LAUNCH_PHYSICAL_ADDRESS.includes(\"TBD\") ||\n      PRO_LAUNCH_PHYSICAL_ADDRESS.includes(\"placeholder\")\n    ) {\n      throw new Error(\n        `[createProLaunchBroadcast] PRO_LAUNCH_PHYSICAL_ADDRESS is still a placeholder (\"${PRO_LAUNCH_PHYSICAL_ADDRESS}\"). ` +\n          \"Set the real CAN-SPAM postal address in convex/broadcast/proLaunchEmailContent.ts before sending.\",\n      );\n    }\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/sendBroadcast.ts#L50-L86","documentation":"createProLaunchBroadcast is a Convex internalAction that calls Resend's POST /broadcasts endpoint to create a broadcast from a locked segment. It reads process.env.RESEND_API_KEY and throws if unset before making any network call, so a missing key fails fast rather than producing an opaque 401 from Resend. The same env var must be configured in the Convex deployment's environment variables.","triggerScenarios":"Invoking createProLaunchBroadcast in a Convex deployment where the RESEND_API_KEY environment variable was never set; running in a fresh preview/dev deployment without the secret; the secret was deleted or renamed.","commonSituations":"New/staging Convex project without secrets provisioned; secret name typo; production secret rotation removed the old key before the new one was set.","solutions":["Set the secret in the target Convex deployment: `npx convex env set RESEND_API_KEY <key>` (or via the Convex dashboard), then redeploy if required.","Confirm you are calling from the deployment that has the secret (check CONVEX_DEPLOYMENT), not a preview without it.","Verify the key is non-empty and a valid Resend key (re_*)."],"exampleFix":"// before — action throws if env missing\nawait ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, {\n  segmentId,\n});\n\n// after — guard at the caller (operator tooling) before invoking\nif (!process.env.RESEND_API_KEY) {\n  throw new Error('RESEND_API_KEY not set in this deployment. Run: npx convex env set RESEND_API_KEY <key>');\n}\nawait ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, { segmentId });","handlingStrategy":"validation","validationCode":"// Guard at the operator/caller layer before invoking the action\nif (!process.env.RESEND_API_KEY) {\n  throw new Error('RESEND_API_KEY not set in this Convex deployment. Run: npx convex env set RESEND_API_KEY <key>');\n}\nawait ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, { segmentId });","typeGuard":"function hasResendKey(env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & { RESEND_API_KEY: string } {\n  return typeof env.RESEND_API_KEY === 'string' && env.RESEND_API_KEY.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Provision RESEND_API_KEY in every Convex deployment that can trigger broadcasts (prod + any preview used for send tests).","Use `npx convex env set RESEND_API_KEY <key>` (or the dashboard), not ad-hoc runtime injection.","During key rotation, set the new key before removing the old one to avoid a window with no key."],"tags":["convex","broadcast-ramp","resend","env-missing","configuration"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}