{"record":{"id":"63ec1653f4e9ba6d","repo":"schollz/croc","slug":"relay-frame-is-too-large-length-bytes","errorCode":null,"errorMessage":"Relay frame is too large (${length} bytes)","messagePattern":"Relay frame is too large \\((.+?) bytes\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/framing.ts","lineNumber":39,"sourceCode":"    this.buffer =\n      this.buffer.byteLength === 0 ? chunk.slice() : concatBytes(this.buffer, chunk);\n    const messages: Uint8Array[] = [];\n\n    while (this.buffer.byteLength >= 8) {\n      for (let index = 0; index < MAGIC.byteLength; index += 1) {\n        if (this.buffer[index] !== MAGIC[index]) {\n          this.buffer = new Uint8Array();\n          throw new Error(\"Relay stream did not start with croc framing\");\n        }\n      }\n      const length = new DataView(\n        this.buffer.buffer,\n        this.buffer.byteOffset,\n        this.buffer.byteLength,\n      ).getUint32(4, true);\n      if (length > MAX_FRAME_SIZE) {\n        this.buffer = new Uint8Array();\n        throw new Error(`Relay frame is too large (${length} bytes)`);\n      }\n      if (this.buffer.byteLength < length + 8) break;\n      messages.push(this.buffer.slice(8, length + 8));\n      this.buffer = this.buffer.slice(length + 8);\n    }\n\n    return messages;\n  }\n}\n","sourceCodeStart":21,"sourceCodeEnd":49,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/framing.ts#L21-L49","documentation":"After verifying the croc magic, FrameDecoder.push() reads the little-endian uint32 length prefix and rejects any frame whose declared length exceeds MAX_FRAME_SIZE (64 MiB), clearing its buffer. The cap prevents a corrupt or hostile length field from causing a multi-gigabyte allocation.","triggerScenarios":"A frame header whose 4 length bytes decode to > 67108864 — caused by stream desync (bytes shifted so data is read as length), a peer that really does frame a >64 MiB message, memory corruption, or a malicious relay/peer probing the client.","commonSituations":"Peer built with a larger frame limit than this client; byte-level desync after a truncated frame or a chunk lost mid-stream; fuzzing the relay stream; endianness mistakes in a custom encoder (big-endian length for a small frame decodes to a huge number, e.g. 8 → 134217728).","solutions":["Confirm the peer frames with the same layout: little-endian ('true' in setUint32/getUint32) uint32 length after the 4-byte magic.","Verify the peer's own frame cap does not exceed 64 MiB; if it legitimately sends bigger frames, raise MAX_FRAME_SIZE on both ends together.","Hex-dump the 8-byte header of the failing frame to check for desync (e.g. magic appearing later in the stream), which indicates an upstream truncation bug.","Treat a repeat occurrence from the same peer as hostile and drop the connection."],"exampleFix":"// before (custom encoder using big-endian)\nview.setUint32(4, payload.byteLength); // defaults to big-endian\n// after\nview.setUint32(4, payload.byteLength, true); // little-endian, matches FrameDecoder","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const messages = decoder.push(chunk);\n} catch (error) {\n  if (error instanceof Error && /frame is too large/.test(error.message)) {\n    // corrupt or hostile length prefix: drop the connection, do not retry the stream\n    socket.close();\n    throw new Error(\"relay stream corrupted (oversized frame header)\");\n  }\n  throw error;\n}","preventionTips":["Write custom encoders with the exact header layout: 4-byte magic + little-endian uint32 length.","Keep both peers' MAX_FRAME_SIZE identical so legitimate frames are never rejected.","Treat repeated oversized-length headers from one peer as hostile and disconnect."],"tags":["protocol","framing","size-limit","stream-corruption","security"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}