{"record":{"id":"ba36493345b93cfe","repo":"schollz/croc","slug":"close-m-sender-cancelled","errorCode":null,"errorMessage":"close.m || \"Sender cancelled\"","messagePattern":"close\\.m \\|\\| \"Sender cancelled\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":797,"sourceCode":"            totalBytes: totalTransferred,\n            totalSize: offer.totalSize,\n          });\n        });\n        const request: RemoteFileRequestWire = {\n          CurrentFileChunkRanges: [],\n          FilesToTransferCurrentNum: fileIndex,\n          MachineID: machineID(),\n          ReconnectVersion: 0,\n        };\n        await sendControl(control, {\n          t: \"recipientready\",\n          b: textEncoder.encode(JSON.stringify(request)),\n        }, key);\n        await receivePromise;\n        await sink.finalize();\n        await sendControl(control, { t: \"close-sender\" }, key);\n        const close = await receiveControl(control, key);\n        if (close.t === \"error\") throw new Error(close.m || \"Sender cancelled\");\n        if (close.t !== \"close-recipient\") {\n          throw new Error(`Expected sender to close the file, got ${close.t}`);\n        }\n        callbacks.onStatus?.(`Verifying ${file.path}`);\n        await verifySink(sink, file.hash);\n        await sink.commit();\n        totalTransferred = beforeFile + file.size;\n        callbacks.onFileComplete?.(file.path);\n      } catch (error) {\n        await sink.abort();\n        throw error;\n      }\n    }\n\n    await sendControl(control, { t: \"finished\" }, key);\n    const finishedMessage = await receiveControl(control, key);\n    if (finishedMessage.t !== \"finished\") {\n      throw new Error(`Expected transfer completion, got ${finishedMessage.t}`);","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L779-L815","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isSenderCancelled(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    // sender failed mid-file: read forwarded text, retry transfer from scratch (no resume support)\n  }\n  throw e;\n}","preventionTips":["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)"],"tags":["peer-error","network","file-transfer","cancel"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}