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
- Inspect the sender's console — the original error is thrown there and forwarded here; the forwarded text (message.m) often names it
- Verify the sender's files are still readable at transfer time and its data connections opened successfully
- Check relay data-port reachability from the sender (firewalls commonly allow 9009 but block data ports)
- 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
- Show clear peer status so recipients understand when a sender cancels
- Keep sender files readable through the whole transfer; snapshot volatile files first
- Wire AbortSignals on both sides so cancellations produce clean errors instead of cryptic teardowns
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
- Recipient cancelled
- close.m || "Sender cancelled"
- Sender did not secure the channel
- Transfer refused
- Relay returned an invalid port list: ${banner}
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/d2b1780d3bff2b65.
Report an issue: GitHub.