schollz/croc · error · Error
close.m || "Sender cancelled"
Error message
close.m || "Sender cancelled"
What it means
Thrown by the recipient after sending 'close-sender' when the sender replies with an 'error' control message (fallback text 'Sender cancelled'). At this point the file data has been received and finalized; the error means the sender-side hit a problem during or immediately after streaming — most often an abort, a socket failure on one of the data connections, or a sender-side chunk-send error — and reported it instead of the expected close-recipient acknowledgement.
Source
Thrown at web/src/protocol/client.ts:797
totalBytes: totalTransferred,
totalSize: offer.totalSize,
});
});
const request: RemoteFileRequestWire = {
CurrentFileChunkRanges: [],
FilesToTransferCurrentNum: fileIndex,
MachineID: machineID(),
ReconnectVersion: 0,
};
await sendControl(control, {
t: "recipientready",
b: textEncoder.encode(JSON.stringify(request)),
}, key);
await receivePromise;
await sink.finalize();
await sendControl(control, { t: "close-sender" }, key);
const close = await receiveControl(control, key);
if (close.t === "error") throw new Error(close.m || "Sender cancelled");
if (close.t !== "close-recipient") {
throw new Error(`Expected sender to close the file, got ${close.t}`);
}
callbacks.onStatus?.(`Verifying ${file.path}`);
await verifySink(sink, file.hash);
await sink.commit();
totalTransferred = beforeFile + file.size;
callbacks.onFileComplete?.(file.path);
} catch (error) {
await sink.abort();
throw error;
}
}
await sendControl(control, { t: "finished" }, key);
const finishedMessage = await receiveControl(control, key);
if (finishedMessage.t !== "finished") {
throw new Error(`Expected transfer completion, got ${finishedMessage.t}`);View on GitHub (pinned to e25f1bdc04)
Solutions
- Read the forwarded message text — it is the sender's original error string and names the real cause
- Check sender-side network stability; data sockets carry the bulk traffic and fail first on bad links
- Verify the sender's files are still readable during streaming (no deletion, no permission changes)
- Retry the transfer; transient socket drops on the sender side are the most common cause
Defensive patterns
Strategy: try-catch
Type guard
function isSenderCancelled(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)) {
// sender failed mid-file: read forwarded text, retry transfer from scratch (no resume support)
}
throw e;
} Prevention
- Keep sender files readable during streaming (no deletion/permission changes)
- Monitor sender uplink stability; data sockets fail before control on lossy links
- On mid-file sender errors, always restart the whole transfer — the protocol has no resume path here (ReconnectVersion is 0)
When it happens
Trigger: Sender's sendFileData throws mid-stream (socket.send rejection, AbortSignal fired, file read error) and reportPeerError forwards it; sender user cancels during upload; a data socket drops and the sender aborts before finishing the file; sender-side storage/File slice() fails.
Common situations: Unstable sender uplink dropping a data connection; sender cancels at the end of a large file; sender's file becomes unreadable (moved/deleted/permission changed) between hashing and streaming; sender browser throttles the tab and a socket times out.
Related errors
- Recipient cancelled
- Sender cancelled
- Recipient requested an unknown file
- Sender did not confirm the croc PAKE handshake
- Sender did not secure the channel
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/ba36493345b93cfe.
Report an issue: GitHub.