schollz/croc · error · Error

Sender did not provide file metadata

Error message

Sender did not provide file metadata

What it means

Thrown by the recipient when the message following externalip is neither 'error' nor a 'fileinfo' carrying a b payload. fileinfo carries the JSON-encoded SenderInfoWire (file list, hashes, sizes, empty folders) that the recipient validates via validateSenderInfo and shows through onOffer. A missing or malformed fileinfo means the sender never delivered usable transfer metadata, so there is nothing to accept.

Source

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

    key = peerKeys.key;
    data = await openDataConnections(
      settings,
      room,
      dataPorts(relay.banner),
      signal,
    );
    await sendControl(control, {
      t: "externalip",
      m: relay.externalIP,
      b: peerPake.b,
    }, key);
    const peerIP = await receiveControl(control, key);
    if (peerIP.t !== "externalip") throw new Error("Sender did not secure the channel");

    const fileInfo = await receiveControl(control, key);
    if (fileInfo.t === "error") throw new Error(fileInfo.m || "Sender cancelled");
    if (fileInfo.t !== "fileinfo" || !fileInfo.b) {
      throw new Error("Sender did not provide file metadata");
    }
    const sender = JSON.parse(textDecoder.decode(fileInfo.b)) as SenderInfoWire;
    const offer = validateSenderInfo(sender);
    callbacks.onStatus?.("Review the incoming files");
    const destination = await callbacks.onOffer(offer);
    if (!destination) {
      await sendControl(control, { t: "error", m: "refusing files" }, key);
      throw new Error("Transfer refused");
    }

    for (const folder of offer.emptyFolders) {
      await destination.createEmptyFolder(folder);
    }
    let totalTransferred = 0;
    receiver = new DataReceiver(data, key, offer.noCompress);
    for (let fileIndex = 0; fileIndex < offer.files.length; fileIndex += 1) {
      checkAbort(signal);
      const file = offer.files[fileIndex];

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Log the received message type and b length at the sender and recipient to see whether fileinfo was sent, dropped, or emptied
  2. Verify senderInfo() serialization runs without throwing and produces non-empty JSON (check for circular refs or enormous lists)
  3. Upgrade both peers to the same protocol version
  4. Retry on a stable network; dropped control frames mid-handshake usually indicate relay issues
Defensive patterns

Strategy: type-guard

Type guard

function isFileInfo(m: { t: string; b?: Uint8Array }): m is { t: "fileinfo"; b: Uint8Array } {
  return m.t === "fileinfo" && !!m.b && m.b.byteLength > 0;
}

Try / catch

catch (e) {
  if (e instanceof Error && e.message === "Sender did not provide file metadata") {
    // sender sent no/empty fileinfo: retry; if persistent, dump the raw control frame and version-check peers
  }
  throw e;
}

Prevention

When it happens

Trigger: Sender sends fileinfo with an empty b payload (serialization bug); sender disconnects and the relay delivers a close/other frame; a version-skewed sender sends a different message type at this point; memory/stringify failure on the sender producing empty bytes.

Common situations: Mock senders that omit the fileinfo payload; sender crashes during JSON.stringify of a huge file list; protocol drift after adding new message types; relay replacing the frame with an error notice.

Related errors


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