koala73/worldmonitor · error · Error
[createProLaunchBroadcast] RESEND_API_KEY not set
Error message
[createProLaunchBroadcast] RESEND_API_KEY not set
What it means
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.
Source
Thrown at convex/broadcast/sendBroadcast.ts:68
/**
* Create a Resend Broadcast from the locked launch content. Does NOT
* send — separate `sendProLaunchBroadcast` step. Returns the new
* broadcast id so the operator can preview in the Resend dashboard
* before firing.
*
* Idempotency: NOT idempotent on segmentId. Calling twice with the same
* segment creates two separate broadcasts. The operator owns "have I
* already created this?" tracking.
*/
export const createProLaunchBroadcast = internalAction({
args: {
segmentId: v.string(),
nameSuffix: v.optional(v.string()),
},
handler: async (_ctx, { segmentId, nameSuffix }) => {
const apiKey = process.env.RESEND_API_KEY;
if (!apiKey) {
throw new Error("[createProLaunchBroadcast] RESEND_API_KEY not set");
}
// Hard-gate: refuse to ship the placeholder physical address.
// CAN-SPAM requires a valid postal address in every commercial
// email footer; sending the literal "physical address TBD" string
// is non-compliant and embarrassing. Edit
// `PRO_LAUNCH_PHYSICAL_ADDRESS` in proLaunchEmailContent.ts to the
// real postal address before invoking this action.
if (
PRO_LAUNCH_PHYSICAL_ADDRESS.includes("TBD") ||
PRO_LAUNCH_PHYSICAL_ADDRESS.includes("placeholder")
) {
throw new Error(
`[createProLaunchBroadcast] PRO_LAUNCH_PHYSICAL_ADDRESS is still a placeholder ("${PRO_LAUNCH_PHYSICAL_ADDRESS}"). ` +
"Set the real CAN-SPAM postal address in convex/broadcast/proLaunchEmailContent.ts before sending.",
);
}
View on GitHub (pinned to ffec79ac33)
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_*).
Example fix
// before — action throws if env missing
await ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, {
segmentId,
});
// after — guard at the caller (operator tooling) before invoking
if (!process.env.RESEND_API_KEY) {
throw new Error('RESEND_API_KEY not set in this deployment. Run: npx convex env set RESEND_API_KEY <key>');
}
await ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, { segmentId }); Defensive patterns
Strategy: validation
Validate before calling
// Guard at the operator/caller layer before invoking the action
if (!process.env.RESEND_API_KEY) {
throw new Error('RESEND_API_KEY not set in this Convex deployment. Run: npx convex env set RESEND_API_KEY <key>');
}
await ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, { segmentId }); Type guard
function hasResendKey(env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & { RESEND_API_KEY: string } {
return typeof env.RESEND_API_KEY === 'string' && env.RESEND_API_KEY.length > 0;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: New/staging Convex project without secrets provisioned; secret name typo; production secret rotation removed the old key before the new one was set.
Related errors
- [assignAndExportWave] waveLabel "${waveLabel}" already has s
- [assignAndExportWave] pool empty — all registrations are sup
- [backfillCanary250] RESEND_API_KEY not set — run with the sa
- [backfillCanary250] Resend list-contacts ${res.status}: ${bo
- [backfillCanary250] unexpected Resend response shape: ${JSON
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/ebc71f406b78d6e8.
Report an issue: GitHub.