Affected versions: 1.0.0-rc.4
Environment-independent (reproduces anywhere the schema runs; we hit it on Node 22 / AWS Lambda)
The non-streaming Blob schema base64-encodes with btoa(String.fromCharCode(...u)). The spread
passes every byte as a separate function argument, so for any sizeable payload the encode
direction throws:
RangeError: Maximum call stack size exceeded
at u (@distilled.cloud/aws/src/traits.ts:651) // encode: (u) => btoa(String.fromCharCode(...u))
Any operation with a large non-streaming Blob request field is therefore unusable. We hit it
on SES SendRawEmail: RawMessage.Data is a plain Blob (services/ses.ts), so every email
carrying a real attachment (a ~50–200 KB PDF) fails 100% of the time. Streaming bodies are
fine — e.g. S3 PutObject.Body is StreamingOutput/HttpPayload, which bypasses this encoder.
Reproduction
import { sendRawEmail } from "@distilled.cloud/aws/ses";
// A raw MIME message with a ~150 KB attachment → RawMessage.Data is a ~200 KB Uint8Array.
await sendRawEmail({ RawMessage: { Data: new Uint8Array(200_000) } /* … */ });
// → RangeError: Maximum call stack size exceeded (during request encoding, before the wire call)
The threshold is engine-dependent (~10⁵ spread arguments in V8), so small blobs pass and real
attachments fail.
Root cause — src/traits.ts
export const Blob = S.String.pipe(
S.decodeTo(
S.instanceOf(Uint8Array),
SchemaTransformation.transform({
decode: (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0)), // safe: char-by-char, no spread
encode: (u) => btoa(String.fromCharCode(...u)), // ← spreads the whole array
}),
),
);
Suggested fix
Chunk the conversion so no single String.fromCharCode call receives the whole array (keeps the
current btoa approach; portable across Node and browser):
encode: (u) => {
let s = "";
for (let i = 0; i < u.length; i += 0x8000) {
s += String.fromCharCode(...u.subarray(i, i + 0x8000));
}
return btoa(s);
},
(Node-only alternative: Buffer.from(u).toString("base64").) The decode side already avoids the
spread and needs no change.
Affected versions: 1.0.0-rc.4
Environment-independent (reproduces anywhere the schema runs; we hit it on Node 22 / AWS Lambda)
The non-streaming
Blobschema base64-encodes withbtoa(String.fromCharCode(...u)). The spreadpasses every byte as a separate function argument, so for any sizeable payload the
encodedirection throws:
Any operation with a large non-streaming
Blobrequest field is therefore unusable. We hit iton SES
SendRawEmail:RawMessage.Datais a plainBlob(services/ses.ts), so every emailcarrying a real attachment (a ~50–200 KB PDF) fails 100% of the time. Streaming bodies are
fine — e.g. S3
PutObject.BodyisStreamingOutput/HttpPayload, which bypasses this encoder.Reproduction
The threshold is engine-dependent (~10⁵ spread arguments in V8), so small blobs pass and real
attachments fail.
Root cause —
src/traits.tsSuggested fix
Chunk the conversion so no single
String.fromCharCodecall receives the whole array (keeps thecurrent
btoaapproach; portable across Node and browser):(Node-only alternative:
Buffer.from(u).toString("base64").) Thedecodeside already avoids thespread and needs no change.