schollz/croc · error · Error

Received file data before it was requested

Error message

Received file data before it was requested

What it means

Thrown by DataReceiver.accept when a data chunk arrives while no file receive is active (this.active is undefined). The protocol only streams data after the recipient sends 'recipientready' for a specific file, so unsolicited data means the sender started streaming early, continued after file completion, or sent data for a file the recipient never requested. It fails the receiver loop and aborts the transfer.

Source

Thrown at web/src/protocol/client.ts:614

        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)));
      }
    }
  }

  private fail(error: Error) {
    this.failure ??= error;
    this.active?.reject(error);
    this.active = undefined;
  }

  private accept(position: number, bytes: Uint8Array) {
    const active = this.active;
    if (!active) throw new Error("Received file data before it was requested");
    active.queue = active.queue.then(async () => {
      if (active.received.has(position)) throw new Error("Received a duplicate file chunk");
      if (
        position < 0 ||
        position % CHUNK_SIZE !== 0 ||
        bytes.byteLength === 0 ||
        bytes.byteLength > CHUNK_SIZE ||
        position + bytes.byteLength > active.file.size
      ) {
        throw new Error("Received a file chunk outside the advertised file size");
      }
      active.received.add(position);
      await active.sink.writeAt(position, bytes);
      active.bytes += bytes.byteLength;
      active.progress(active.bytes);
      if (active.bytes === active.file.size) {
        this.active = undefined;
        active.resolve();

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Check sender loop ordering: data must only follow a recipientready and stop before close-sender
  2. When resuming/reconnecting, drain or re-create data sockets so stale frames from the previous session cannot arrive
  3. Ensure the sender's chunk scheduling across multiple sockets completes before it sends its close message
  4. Add logging of active-file lifecycle (set at receive(), cleared at completion) to identify which boundary leaks the unsolicited chunk
Defensive patterns

Strategy: try-catch

Try / catch

catch (e) {
  if (e instanceof Error && e.message === "Received file data before it was requested") {
    // sender streamed outside the protocol window: abort and restart, resuming is unsafe
  }
  throw e;
}

Prevention

When it happens

Trigger: Sender begins streaming before receiving recipientready; sender sends extra chunks after active.bytes === file.size resolved the promise (this.active reset to undefined); leftover in-flight frames from a previous file arriving after completion; sender and recipient disagree on file count so data for file N+1 arrives before its request.

Common situations: Sender implementation that pipelines the next file without waiting for close-sender/close-recipient; race where the final chunks of a completed file are still queued on a data socket while the recipient starts the next file (the dup-check then fires, but unsolicited-data fires when active is undefined between files); reconnect scenarios where the sender resumes sending immediately.

Related errors


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