Inside the World
Payloads and byte streams
Use values for control data and byte streams for bulk data. Offloading to R2 removes storage-message pressure, not every memory limit.
A value still has to fit in memory
Binary payload fields over 1 MiB go to R2 before the referencing event commits. That avoids putting a large value in a SQLite cell or a single RPC message. Reading it back still involves buffers, decoding and the VM’s representation.
| Size | Meaning |
|---|---|
| 1 MiB | Offload threshold for large binary event values and stream chunks. |
| 18 MiB | Enforced serialized payload limit. This is not the same as 18 MiB of raw step input. |
| 18,790,000 bytes | Random step output demonstrated on retained executors at 8cddcbd. Not a universal raw-value cap. |
| 14,155,722 bytes | Raw step-input boundary for the tested unencrypted byte-array shape; one byte more failed. |
| 32 MiB | Maximum encoded World stream chunk. Some probes encode raw bytes first. |
At build 8cddcbd, the getWritable probe passed 25,165,800 raw bytes and rejected one byte more at 33,554,433 encoded bytes. Other framing or encryption can change the raw allowance. Returned byte streams have a different path.
Pass a handle, then move bytes incrementally
A step returning a byte stream lets the SDK persist a handle rather than serialize all bytes as one return value. Large chunks reach R2. Readers stream them back in 1 MiB pieces instead of loading the whole stream.
The World’s bounded-retention test writes and reads 512 MiB while holding at most two write chunks or two read pieces. A real Workers probe transferred 1.75 GiB in 16 MiB producer chunks and checked every byte at build 8cddcbd.
The producer must outlive the step result
A returned stream can keep writing after the step’s function has returned. Ending its delivery too soon cancels that background work. The flow request therefore waits for tracked background writes, within a 13.5-minute budget from delivery start.
Returned-step streams renew a 30-second writer lease. If the writer stops and the lease expires, readers get a StreamError rather than waiting forever. Registration starts at the producer’s first attempted write. A producer that never attempts a write has no lease yet and is not detected. Named writable streams have different lifecycle rules; do not assume every stream uses that lease.
The current multi-GiB reader problem
In the installed SDK, flushablePipe races each small read against a long-lived closed promise. Reactions accumulate until close, retaining roughly 40 MiB per GiB read. At build 8cddcbd, a QuickJS step read 1.75 GiB correctly but a 2 GiB probe produced an executor exceededMemory event. The earlier 4 GiB failure is not a safe allowance.
The published SDK is not patched here. Keep one reading step’s volume bounded, or wait for an upstream fix before relying on multi-GiB reads. World-side chunk bounds do not repair retention in the consumer SDK.
Sources and implementation
- packages/world-cloudflare/src/lib/payloads.ts
- packages/world-cloudflare/src/streams/streamer.ts
- test/streams.test.ts
- docs/limits.md
Repository links may require access. Vercel links point to public source or published documentation. Logical API names do not establish backend internals.