{"record":{"id":"b5e3a90f1f73cd6e","repo":"koala73/worldmonitor","slug":"backfillcanary250-unexpected-resend-response-sha","errorCode":null,"errorMessage":"[backfillCanary250] unexpected Resend response shape: ${JSON.stringify(json).slice(0, 200)}","messagePattern":"\\[backfillCanary250\\] unexpected Resend response shape: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/backfillCanaryWaveStamps.ts","lineNumber":178,"sourceCode":"\n      const res = await fetch(url.toString(), {\n        method: \"GET\",\n        headers: {\n          Authorization: `Bearer ${apiKey}`,\n          \"User-Agent\": USER_AGENT,\n        },\n      });\n\n      if (!res.ok) {\n        const body = await res.text().catch(() => \"<no body>\");\n        throw new Error(\n          `[backfillCanary250] Resend list-contacts ${res.status}: ${body}`,\n        );\n      }\n\n      const json = (await res.json()) as ResendListContactsResponse;\n      if (!json || !Array.isArray(json.data)) {\n        throw new Error(\n          `[backfillCanary250] unexpected Resend response shape: ${JSON.stringify(json).slice(0, 200)}`,\n        );\n      }\n\n      for (const contact of json.data) {\n        stats.fetched++;\n        const normalizedEmail = (contact.email ?? \"\").trim().toLowerCase();\n        if (!normalizedEmail) {\n          stats.failed++;\n          continue;\n        }\n        try {\n          const out = await ctx.runMutation(\n            internal.broadcast.backfillCanaryWaveStamps\n              ._stampWaveByNormalizedEmail,\n            {\n              normalizedEmail,\n              waveLabel: CANARY_WAVE_LABEL,","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/backfillCanaryWaveStamps.ts#L160-L196","documentation":"Thrown by backfillCanary250 when the Resend response parses as JSON but does not have the expected shape — specifically json is falsy or json.data is not an array. This guards against silent schema drift where Resend changes its API response format, returning a 200 but with a different structure that would break the pagination/stamping loop.","triggerScenarios":"Resend returns a 200 OK but with a body where `data` is missing, is an object instead of an array, or the response is an error envelope that still returns 200; Resend ships a breaking API change; an unexpected proxy/CDN injects a non-JSON body that happened to parse.","commonSituations":"Resend API version bump changed the response envelope; a man-in-the-middle (corporate proxy) returned an HTML interstitial that parsed as partial JSON; Resend returned a top-level error object instead of a data array.","solutions":["Inspect the JSON fragment in the error message to see what Resend actually returned.","Check Resend's API changelog for response-shape changes and update the ResendListContactsResponse type/parsing accordingly.","If a proxy is interfering, route the request outside the proxy or whitelist the Resend domain.","Log the full response once to confirm whether it's a transient anomaly or a permanent schema change."],"exampleFix":"// before\nconst json = (await res.json()) as ResendListContactsResponse;\nif (!json || !Array.isArray(json.data)) throw new Error(\"unexpected shape\");\n// after — log full body for diagnosis, then adapt parser\nconst raw = await res.text();\nconsole.log(\"[backfill] raw Resend body:\", raw.slice(0, 500));\nconst json = JSON.parse(raw);\nconst contacts = Array.isArray(json) ? json : json.data;\nif (!Array.isArray(contacts)) throw new Error(\"unsupported shape\");","handlingStrategy":"try-catch","validationCode":"// Validate shape after parsing\nconst json = await res.json();\nif (!json || !Array.isArray(json.data)) {\n  throw new Error(\"Resend response schema drifted — investigate before continuing\");\n}","typeGuard":"function isResendListContactsResponse(json: unknown): json is { data: unknown[] } {\n  return !!json && typeof json === \"object\" && Array.isArray((json as any).data);\n}","tryCatchPattern":"try {\n  await backfillCanary250(ctx, {});\n} catch (e) {\n  if (e.message.includes(\"unexpected Resend response shape\")) {\n    // log full body, check Resend changelog, then decide\n    console.error(e.message);\n  } else throw e;\n}","preventionTips":["Pin/monitor the Resend API version in use.","Log the raw response body when shape drift is suspected.","Update the ResendListContactsResponse type when Resend changes the envelope."],"tags":["convex","broadcast","resend","schema-drift","api-contract","backfill"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}