You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(drivers): resend and postmark sendBatch do not chunk at the provider's batch cap, so an over-large batch fails wholesale #123
Both batch drivers pass the caller's whole list to the provider in one request, whatever its length. The providers cap it:
Resend /emails/batch: 100 emails per call
Postmark /email/batch and /email/batchWithTemplates: 500 messages per call (over the cap it answers ErrorCode 410, Too many batch messages)
Neither driver chunks or checks. A 150-message Resend batch or a 600-message Postmark batch goes out as a single oversized request, and when the provider rejects it, sendBatch maps that one error onto every message.
resend -> requests: 1 messages in request 1: 150
postmark -> requests: 1 messages in request 1: 600
Why it matters
email.sendBatch() is the documented way to send a list, and sendStream() defaults to a chunk size of 50 — so a caller who reaches for sendBatch with a real mailing list has no signal that 101 or 501 is a cliff. When it is hit, nothing partial gets through: sendBatch maps the single request error onto all N results, so 600 messages fail together on an opaque provider error, and a retry middleware then re-sends all 600 into the same wall.
Related, same shape, not enforced either: Resend caps to at 50 addresses per message, Postmark caps To/Cc/Bcc at 50 each, and SES caps a message at 50 recipients — ses.toPayload merges to+cc+bcc into one ToAddresses list with no check (src/drivers/ses.ts:142).
Where
src/drivers/resend.ts:95-96 — /emails/batch, no chunking
src/drivers/postmark.ts:102-107 — /email/batch, no chunking
src/drivers/ses.ts:142 — merged recipient list, no cap
Suggested fix
Give each batch driver a BATCH_LIMIT constant and have sendBatch slice the input into that many messages per request, issuing the requests and stitching results back into input order — the positional contract holds and one over-large chunk no longer takes the rest with it. Recipient caps are better expressed as an INVALID_OPTIONS result for the offending message, so the failure names the field instead of arriving as a provider error string.
Activity
fix(drivers): thirteen verified defects across the driver layer (#130)
productdevbook commentedon Sep 2, 2026
Fixed in #130, with a regression test. Suite went from 269 tests to 293.