{"record":{"id":"1dd6e12e6971255f","repo":"koala73/worldmonitor","slug":"sendprolaunchbroadcast-resend-res-status-e","errorCode":null,"errorMessage":"[sendProLaunchBroadcast] Resend ${res.status}: ${errBody}","messagePattern":"\\[sendProLaunchBroadcast\\] Resend (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/sendBroadcast.ts","lineNumber":174,"sourceCode":"    const body: Record<string, unknown> = {};\n    if (scheduledAt) body.scheduled_at = scheduledAt;\n\n    const res = await fetch(\n      `${RESEND_API_BASE}/broadcasts/${encodeURIComponent(broadcastId)}/send`,\n      {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          Authorization: `Bearer ${apiKey}`,\n          \"User-Agent\": USER_AGENT,\n        },\n        body: JSON.stringify(body),\n      },\n    );\n\n    if (!res.ok) {\n      const errBody = await res.text().catch(() => \"<no body>\");\n      throw new Error(\n        `[sendProLaunchBroadcast] Resend ${res.status}: ${errBody}`,\n      );\n    }\n\n    return {\n      broadcastId,\n      status: scheduledAt ? \"scheduled\" : \"queued\",\n      scheduledAt: scheduledAt ?? null,\n    };\n  },\n});\n","sourceCodeStart":156,"sourceCodeEnd":186,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/sendBroadcast.ts#L156-L186","documentation":"Thrown when Resend's POST /broadcasts/{broadcastId}/send endpoint returns a non-2xx status during sendProLaunchBroadcast. The error includes the HTTP status and raw response body. This is the send (fire) step — the broadcast was already created successfully.","triggerScenarios":"The broadcastId does not exist or was already sent (Resend 404/409). The broadcast was deleted between create and send. Rate limiting (429). Resend 5xx outage. The RESEND_API_KEY lacks send permissions for this broadcast.","commonSituations":"The operator created a broadcast, then deleted it in the Resend dashboard before sending. The same broadcastId was sent twice (double-click, duplicate schedule). A Resend outage occurs between broadcast creation and the send call. The broadcast was already sent by a previous run that did not record its result.","solutions":["Check the status code: 404/409 means the broadcastId is gone or already sent — inspect the Resend dashboard to determine the true state before retrying.","If 409 (already sent), do NOT retry; instead record the send state in the waveRuns row to prevent re-sending.","For 429 or 5xx, retry with exponential backoff — the send endpoint is designed to be retryable for transient failures.","If the broadcast was deleted, create a new one via createProLaunchBroadcast and send the new id."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await ctx.runAction(internal.broadcast.sendBroadcast.sendProLaunchBroadcast, { broadcastId });\n} catch (err) {\n  const msg = String(err);\n  if (msg.includes(\"Resend 409\")) {\n    // already sent — record state, do not retry\n    throw new Error(`Broadcast ${broadcastId} already sent or in conflict`);\n  }\n  if (msg.includes(\"Resend 404\")) {\n    // broadcastId gone — recreate via createProLaunchBroadcast\n    throw err;\n  }\n  if (msg.includes(\"Resend 429\") || msg.includes(\"Resend 5\")) {\n    // transient — retry with backoff\n    throw err; // let scheduler retry\n  }\n  throw err;\n}","preventionTips":["Record the broadcast send state in the waveRuns row immediately after success to prevent duplicate sends.","Before retrying a failed send, inspect the Resend dashboard to confirm the broadcast was not already sent.","Treat 409 (already sent) as a terminal state, not a retryable error."],"tags":["resend","email-broadcast","network","api-error","convex"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}