Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/email-routing/gotchas.md
1# Gotchas & Troubleshooting23## Critical Pitfalls45### Stream Consumption (MOST COMMON)67**Problem:** "stream already consumed" or worker hangs89**Cause:** `message.raw` is `ReadableStream` - consume once only1011**Solution:**12```typescript13// ❌ WRONG14const email1 = await parser.parse(await message.raw.arrayBuffer());15const email2 = await parser.parse(await message.raw.arrayBuffer()); // FAILS1617// ✅ CORRECT18const raw = await message.raw.arrayBuffer();19const email = await parser.parse(raw);20```2122Consume `message.raw` immediately before any async operations.2324### Destination Verification2526**Problem:** Emails not forwarding2728**Cause:** Destination unverified2930**Solution:** Add destination, check inbox for verification email, click link. Verify status: `GET /zones/{id}/email/routing/addresses`3132### Mail Authentication3334**Problem:** Legitimate emails rejected3536**Cause:** Missing SPF/DKIM/DMARC on sender domain3738**Solution:** Configure sender DNS:39```dns40example.com. IN TXT "v=spf1 include:_spf.example.com ~all"41selector._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=..."42_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine"43```4445### Envelope vs Header4647**Problem:** Filtering on wrong address4849**Solution:**50```typescript51// Routing/auth: envelope52if (message.from === "[email protected]") { }5354// Display: headers55const display = message.headers.get("from");56```5758### SendEmail Limits5960| Issue | Limit | Solution |61|-------|-------|----------|62| From domain | Must own | Use Email Routing domain |63| Volume | ~100/min Free | Upgrade or throttle |64| Attachments | Not supported | Link to R2 |65| Type | Transactional | No bulk |6667## Common Errors6869### CPU Time Exceeded7071**Cause:** Heavy parsing, large emails7273**Solution:**74```typescript75const size = parseInt(message.headers.get("content-length") || "0") / 1024 / 1024;76if (size > 20) {77message.setReject("Too large");78return;79}8081ctx.waitUntil(expensiveWork());82await message.forward("[email protected]");83```8485### Rule Not Triggering8687**Causes:** Priority conflict, matcher error, catch-all override8889**Solution:** Check priority (lower=first), verify exact match, confirm destination verified9091### Undefined Property9293**Cause:** Missing header9495**Solution:**96```typescript97// ❌ WRONG98const subj = message.headers.get("subject").toLowerCase();99100// ✅ CORRECT101const subj = message.headers.get("subject")?.toLowerCase() || "";102```103104## Limits105106| Resource | Free | Paid |107|----------|------|------|108| Email size | 25 MB | 25 MB |109| Rules | 200 | 200 |110| Destinations | 200 | 200 |111| CPU time | 10ms | 30s (default), 5min (max) |112| SendEmail | ~100/min | Higher |113114## Debugging115116### Local117118```bash119npx wrangler dev120121curl -X POST 'http://localhost:8787/__email' \122--header 'content-type: message/rfc822' \123--data 'From: [email protected]124To: [email protected]125Subject: Test126127Body'128```129130### Production131132```bash133npx wrangler tail134```135136### Pattern137138```typescript139export default {140async email(message, env, ctx) {141try {142console.log("From:", message.from);143await process(message, env);144} catch (err) {145console.error(err);146message.setReject(err.message);147}148}149} satisfies ExportedHandler;150```151152## Auth Troubleshooting153154### Check Status155156```typescript157const auth = message.headers.get("authentication-results") || "";158console.log({159spf: auth.includes("spf=pass"),160dkim: auth.includes("dkim=pass"),161dmarc: auth.includes("dmarc=pass")162});163164if (!auth.includes("pass")) {165message.setReject("Failed auth");166return;167}168```169170### SPF Issues171172**Causes:** Forwarding breaks SPF, too many lookups (>10), missing includes173174**Solution:**175```dns176; ✅ Good177example.com. IN TXT "v=spf1 include:_spf.google.com ~all"178179; ❌ Bad - too many180example.com. IN TXT "v=spf1 include:a.com include:b.com ... ~all"181```182183### DMARC Alignment184185**Cause:** From domain must match SPF/DKIM domain186187## Best Practices1881891. Consume `message.raw` immediately1902. Verify destinations1913. Handle missing headers (`?.`)1924. Use envelope for routing1935. Check spam scores1946. Test locally first1957. Use `ctx.waitUntil` for background work1968. Size-check early197