schollz/croc · error · Error

Sender cancelled

Error message

Sender cancelled

What it means

Thrown by the recipient when the control message after the externalip exchange is an 'error' from the sender (fallback text 'Sender cancelled' when the sender sent no message text). The sender reported a failure over the now-encrypted channel — typically an abort before or during fileinfo — and the recipient surfaces it and stops before showing any offer.

Source

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

      throw new Error("Sender PAKE confirmation failed");
    }
    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) {

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Inspect the sender's console — the original error is thrown there and forwarded here; the forwarded text (message.m) often names it
  2. Verify the sender's files are still readable at transfer time and its data connections opened successfully
  3. Check relay data-port reachability from the sender (firewalls commonly allow 9009 but block data ports)
  4. Retry the transfer; user-initiated cancellations are the most common cause and simply re-running fixes them
Defensive patterns

Strategy: try-catch

Type guard

function isPeerCancel(e: unknown): boolean {
  return e instanceof Error && /Sender cancelled/.test(e.message);
}

Try / catch

catch (e) {
  if (e instanceof Error && /Sender cancelled/.test(e.message)) {
    showStatus("The sender stopped the transfer"); return; // expected user flow
  }
  throw e;
}

Prevention

When it happens

Trigger: Sender's user cancels before sending metadata; sender throws between key establishment and fileinfo (e.g. openDataConnections failure surfaces here via reportPeerError); sender-side preparation error such as reading files that no longer exist; sender refuses to proceed for policy reasons.

Common situations: Sender closes the tab or hits cancel at 'Securing channel…'/'Opening encrypted data channels…'; sender's File handles revoked (file deleted, permission lost) before metadata is sent; sender behind a relay whose data ports are blocked so it errors out during setup.

Related errors


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