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

  1. Verify both peers run the same client build so wasm encrypt/decrypt/compress agree
  2. 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
  3. Inspect the transport layer for frame reassembly: log payload.byteLength per frame to find where truncation begins
  4. 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

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


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/6f758e114d2bf844. Report an issue: GitHub.