schollz/croc · error · Error
Received an invalid file chunk
Error message
Received an invalid file chunk
What it means
Thrown by DataReceiver.read when a decrypted (and decompressed) data payload is shorter than 9 bytes. Every valid data chunk is an 8-byte little-endian BigUint64 position prefix plus at least 1 byte of data, so anything smaller is a corrupt or non-conformant frame. This error fails the whole receiver because chunk integrity cannot be established.
Source
Thrown at web/src/protocol/client.ts:586
reject,
};
});
}
stop() {
this.stopped = true;
this.fail(new Error("Data receiver stopped"));
}
private async read(socket: CrocSocket) {
const engine = wasm();
while (!this.stopped) {
try {
let payload = await engine.decrypt(await socket.receive(), this.key);
if (!this.noCompress) {
payload = await engine.decompress(payload, MAX_DECOMPRESSED_CHUNK_SIZE);
}
if (payload.byteLength < 9) throw new Error("Received an invalid file chunk");
const positionBig = new DataView(
payload.buffer,
payload.byteOffset,
payload.byteLength,
).getBigUint64(0, true);
if (positionBig > BigInt(Number.MAX_SAFE_INTEGER)) {
throw new Error("Received a file position that is too large");
}
const position = Number(positionBig);
const bytes = payload.slice(8);
await this.accept(position, bytes);
} catch (error) {
if (this.stopped) return;
this.stopped = true;
this.fail(error instanceof Error ? error : new Error(String(error)));
}
}
}View on GitHub (pinned to e25f1bdc04)
Solutions
- Verify both peers run the same client build so wasm encrypt/decrypt/compress agree
- Confirm the PAKE inputs (passphrase, room, purpose 'peer-transfer', curve) are identical on both sides — a mismatch usually fails decryption earlier, but partial garbage can land here
- Inspect the transport layer for frame reassembly: log payload.byteLength per frame to find where truncation begins
- Test with a known-good relay and small file to rule out network-path corruption
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm both peers use the same engine build before transferring
// (best-effort; integrity errors otherwise surface in DataReceiver)
const engine = wasm();
if (typeof engine.compress !== "function" || typeof engine.decompress !== "function") {
throw new Error("WASM engine incomplete — rebuild the client");
} Try / catch
catch (e) {
if (e instanceof Error && e.message === "Received an invalid file chunk") {
// data-channel integrity lost: fail the transfer, suggest restart with fresh code phrase
}
throw e;
} Prevention
- Ship sender and recipient from the same build so wasm encrypt/compress framing matches
- Never change the chunk wire format (8-byte LE position + data) on one side only
- On integrity errors, restart the whole transfer with a new code phrase instead of resuming
When it happens
Trigger: Decrypt succeeded but with a wrong key producing garbage plaintext that happens to be short; decompression of a corrupted payload yielding <9 bytes; sender implementation sends an empty or malformed chunk; socket fragmentation reassembly bug in the transport layer delivering a truncated frame.
Common situations: Key derivation mismatch between peers (different PAKE purpose/room inputs) so decryption yields garbage; WASM engine (compress/decompress) version skew between sender and recipient; a relay that truncates frames; memory corruption or slicing bugs in a custom CrocSocket implementation.
Related errors
- Received a file position that is too large
- Received a duplicate file chunk
- Received a file chunk outside the advertised file size
- Received more data than the advertised file size
- Could not load croc.wasm (${response.status})
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/6f758e114d2bf844.
Report an issue: GitHub.