{"record":{"id":"d2b1780d3bff2b65","repo":"schollz/croc","slug":"sender-cancelled","errorCode":null,"errorMessage":"Sender cancelled","messagePattern":"Sender cancelled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":731,"sourceCode":"      throw new Error(\"Sender PAKE confirmation failed\");\n    }\n    key = peerKeys.key;\n    data = await openDataConnections(\n      settings,\n      room,\n      dataPorts(relay.banner),\n      signal,\n    );\n    await sendControl(control, {\n      t: \"externalip\",\n      m: relay.externalIP,\n      b: peerPake.b,\n    }, key);\n    const peerIP = await receiveControl(control, key);\n    if (peerIP.t !== \"externalip\") throw new Error(\"Sender did not secure the channel\");\n\n    const fileInfo = await receiveControl(control, key);\n    if (fileInfo.t === \"error\") throw new Error(fileInfo.m || \"Sender cancelled\");\n    if (fileInfo.t !== \"fileinfo\" || !fileInfo.b) {\n      throw new Error(\"Sender did not provide file metadata\");\n    }\n    const sender = JSON.parse(textDecoder.decode(fileInfo.b)) as SenderInfoWire;\n    const offer = validateSenderInfo(sender);\n    callbacks.onStatus?.(\"Review the incoming files\");\n    const destination = await callbacks.onOffer(offer);\n    if (!destination) {\n      await sendControl(control, { t: \"error\", m: \"refusing files\" }, key);\n      throw new Error(\"Transfer refused\");\n    }\n\n    for (const folder of offer.emptyFolders) {\n      await destination.createEmptyFolder(folder);\n    }\n    let totalTransferred = 0;\n    receiver = new DataReceiver(data, key, offer.noCompress);\n    for (let fileIndex = 0; fileIndex < offer.files.length; fileIndex += 1) {","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L713-L749","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isPeerCancel(e: unknown): boolean {\n  return e instanceof Error && /Sender cancelled/.test(e.message);\n}","tryCatchPattern":"catch (e) {\n  if (e instanceof Error && /Sender cancelled/.test(e.message)) {\n    showStatus(\"The sender stopped the transfer\"); return; // expected user flow\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["protocol","peer-error","cancel","network"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}