{"record":{"id":"ae9d46fc2984e50e","repo":"schollz/croc","slug":"recipient-refused-transfer","errorCode":null,"errorMessage":"Recipient refused transfer","messagePattern":"Recipient refused transfer","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":473,"sourceCode":"      settings,\n      room,\n      dataPorts(relay.banner),\n      signal,\n    );\n\n    const peerIP = await receiveControl(control, key);\n    if (peerIP.t !== \"externalip\") throw new Error(\"Recipient did not secure the channel\");\n    await sendControl(control, { t: \"externalip\", m: relay.externalIP }, key);\n    await sendControl(control, {\n      t: \"fileinfo\",\n      b: textEncoder.encode(JSON.stringify(senderInfo(files))),\n    }, key);\n\n    let totalTransferred = 0;\n    for (;;) {\n      checkAbort(signal);\n      const message = await receiveControl(control, key);\n      if (message.t === \"error\") throw new Error(message.m || \"Recipient refused transfer\");\n      if (message.t === \"finished\") {\n        await sendControl(control, { t: \"finished\" }, key);\n        callbacks.onStatus?.(\"Transfer complete\");\n        return;\n      }\n      if (message.t !== \"recipientready\" || !message.b) {\n        throw new Error(`Unexpected peer message: ${message.t}`);\n      }\n\n      const request = JSON.parse(\n        textDecoder.decode(message.b),\n      ) as RemoteFileRequestWire;\n      const fileIndex = request.FilesToTransferCurrentNum;\n      const prepared = files[fileIndex];\n      if (!prepared) throw new Error(\"Recipient requested an unknown file\");\n      callbacks.onStatus?.(`Sending ${prepared.name}`);\n      const beforeFile = totalTransferred;\n      await sendFileData(","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L455-L491","documentation":"Inside the sender's transfer loop, the recipient sent an 'error' control message. The error text from the recipient is used if present (message.m), otherwise this default is thrown. It is the recipient's refusal or failure propagated over the encrypted control channel, commonly a disk-space or write-permission failure on the receiving side.","triggerScenarios":"Recipient's verifySink or storage write failing (no disk space, permission denied, quota exceeded in the browser); recipient actively cancelling; recipient's wasm reporting a decompression/hash error.","commonSituations":"Receiving in a browser storage sandbox with exhausted quota; recipient tab losing its FileSystem handle mid-transfer; recipient hits cancel while blocks are in flight.","solutions":["Read the embedded message.m if present: it carries the recipient's actual failure text","On the recipient, free disk/quota space and grant write access, then retry the transfer","If the recipient cancelled, simply restart the transfer when both sides are ready"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await sendFiles(opts);\n} catch (e) {\n  if (/refused transfer/.test(e.message)) {\n    showBanner(e.message); // message.m from the recipient explains why\n    return;\n  }\n  throw e;\n}","preventionTips":["On the recipient, check available disk/quota before accepting large transfers","Propagate message.m to the UI so the recipient's reason is visible to the sender","Handle recipient cancellation gracefully instead of surfacing a generic failure"],"tags":["transfer","recipient","storage","propagated-error"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}