{"record":{"id":"ba99b381053cb882","repo":"koala73/worldmonitor","slug":"createprolaunchbroadcast-resend-response-missing","errorCode":null,"errorMessage":"[createProLaunchBroadcast] Resend response missing id: ${JSON.stringify(json)}","messagePattern":"\\[createProLaunchBroadcast\\] Resend response missing id: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/sendBroadcast.ts","lineNumber":121,"sourceCode":"        reply_to: PRO_LAUNCH_REPLY_TO,\n        subject: PRO_LAUNCH_SUBJECT,\n        html: PRO_LAUNCH_HTML,\n        text: PRO_LAUNCH_TEXT,\n      }),\n    });\n\n    if (!res.ok) {\n      const body = await res.text().catch(() => \"<no body>\");\n      throw new Error(\n        `[createProLaunchBroadcast] Resend ${res.status}: ${body}`,\n      );\n    }\n\n    const json = (await res.json().catch(() => null)) as {\n      id?: string;\n    } | null;\n    if (!json?.id) {\n      throw new Error(\n        `[createProLaunchBroadcast] Resend response missing id: ${JSON.stringify(json)}`,\n      );\n    }\n\n    return {\n      broadcastId: json.id,\n      name,\n      segmentId,\n      subject: PRO_LAUNCH_SUBJECT,\n    };\n  },\n});\n\n/**\n * Send (or schedule) a previously-created Resend Broadcast.\n *\n * `scheduledAt`: ISO 8601 timestamp string OR a natural-language phrase\n * Resend accepts (\"in 2 hours\"). Omit for immediate send.","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/sendBroadcast.ts#L103-L139","documentation":"Thrown when Resend's POST /broadcasts returns HTTP 2xx but the JSON body either cannot be parsed or lacks the expected 'id' field. The error includes the stringified response JSON (or 'null' if parsing failed). This guards against an unexpected Resend API contract change or a malformed response.","triggerScenarios":"Resend ships an API version that renames or nests the broadcast id field. A transparent proxy or gateway between Convex and Resend rewrites the response body. Resend returns an HTML error page with a 200 status (rare misconfigured gateway). The response body is empty or truncated.","commonSituations":"Resend introduced a breaking API change after an upgrade. A corporate egress proxy strips or rewrites JSON. The Resend base URL RESEND_API_BASE was changed to point at a mock/staging server that does not return the full contract.","solutions":["Inspect the stringified JSON in the error message to see what Resend actually returned — if the id is present under a different key, the response type assertion in sendBroadcast.ts:117-119 needs updating.","Confirm RESEND_API_BASE ('https://api.resend.com') has not been overridden to a non-standard host.","Check Resend's API changelog for a response-shape change in the broadcasts endpoint.","If the response is an HTML error page, investigate network egress (proxy, firewall) between Convex and Resend."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const result = await ctx.runAction(internal.broadcast.sendBroadcast.createProLaunchBroadcast, { segmentId });\n  if (!result?.broadcastId) throw new Error(\"No broadcastId returned\");\n} catch (err) {\n  if (String(err).includes(\"missing id\")) {\n    // Resend returned 2xx but unexpected shape — log raw response, check Resend API changelog\n    // Do NOT retry blindly; investigate the response contract\n  }\n  throw err;\n}","preventionTips":["Pin the Resend API version if Resend offers versioned endpoints.","Do not override RESEND_API_BASE to point at a non-standard host without testing the response contract.","Monitor Resend's API changelog for breaking changes to the broadcasts endpoint."],"tags":["resend","api-contract","network","convex","email-broadcast"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}