schollz/croc · error · Error
Message is too large (${payload.byteLength} bytes)
Error message
Message is too large (${payload.byteLength} bytes) What it means
encodeFrame() wraps a payload in croc framing (4-byte magic 'croc' + little-endian uint32 length + payload) and refuses payloads larger than MAX_FRAME_SIZE (64 MiB). The length field is uint32, and the cap bounds memory use on both encode and decode; anything larger cannot be framed.
Source
Thrown at web/src/protocol/framing.ts:8
import { concatBytes } from "./bytes";
export const MAX_FRAME_SIZE = 64 * 1024 * 1024;
const MAGIC = new Uint8Array([0x63, 0x72, 0x6f, 0x63]);
export function encodeFrame(payload: Uint8Array) {
if (payload.byteLength > MAX_FRAME_SIZE) {
throw new Error(`Message is too large (${payload.byteLength} bytes)`);
}
const frame = new Uint8Array(8 + payload.byteLength);
frame.set(MAGIC, 0);
new DataView(frame.buffer).setUint32(4, payload.byteLength, true);
frame.set(payload, 8);
return frame;
}
export class FrameDecoder {
private buffer = new Uint8Array();
push(chunk: Uint8Array) {
this.buffer =
this.buffer.byteLength === 0 ? chunk.slice() : concatBytes(this.buffer, chunk);
const messages: Uint8Array[] = [];
while (this.buffer.byteLength >= 8) {
for (let index = 0; index < MAGIC.byteLength; index += 1) {View on GitHub (pinned to e25f1bdc04)
Solutions
- Split the payload into chunks of at most MAX_FRAME_SIZE and frame each chunk separately, reassembling on the receiver via FrameDecoder.
- Check payload.byteLength before calling encodeFrame and fail with your own actionable error message.
- If a larger cap is genuinely required in a controlled deployment, raise MAX_FRAME_SIZE on BOTH ends (encodeFrame and FrameDecoder share it) and keep it below 2^32.
- Compress the payload first (the codec already supports wasm.compress) and re-check the size.
Example fix
// before
const frame = encodeFrame(payload); // payload may be 100 MiB
// after
const CHUNK = 8 * 1024 * 1024;
for (let off = 0; off < payload.byteLength; off += CHUNK) {
const frame = encodeFrame(payload.subarray(off, off + CHUNK));
send(frame);
} Defensive patterns
Strategy: validation
Validate before calling
const MAX_FRAME_SIZE = 64 * 1024 * 1024;
function assertFramable(payload: Uint8Array): void {
if (payload.byteLength > MAX_FRAME_SIZE) {
throw new RangeError(`payload ${payload.byteLength} exceeds frame cap; split into <=${MAX_FRAME_SIZE} chunks`);
}
} Try / catch
try {
send(encodeFrame(payload));
} catch (error) {
if (error instanceof RangeError || /too large/.test(String(error))) {
await sendChunked(payload, 8 * 1024 * 1024); // fall back to chunked send
return;
}
throw error;
} Prevention
- Chunk application payloads well below 64 MiB (8–16 MiB is typical) before framing.
- Size-check payloads in your send helper so the failure surfaces with your own actionable message.
- If raising the cap, change MAX_FRAME_SIZE identically on encode and decode sides and stay under 2^32.
When it happens
Trigger: Calling encodeFrame(payload) where payload.byteLength > 64 * 1024 * 1024. Typically caused by sending a single huge chunk, an un-split file block, or a bulk base64 blob through the relay instead of chunking at the application layer.
Common situations: Raising the application chunk size above 64 MiB; serializing a whole file's metadata plus data in one message; compressing already-incompressible data that still exceeds the cap; porting the Go croc client's larger frame limit without matching MAX_FRAME_SIZE.
Related errors
- Relay stream did not start with croc framing
- Relay frame is too large (${length} bytes)
- Relay returned an invalid port list: ${banner}
- Relay rejected the connection: ${response}
- Relay could not open the room: ${confirmation}
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/d35f382f50a50be2.
Report an issue: GitHub.