{"record":{"id":"dc4a0e7f780e7d30","repo":"schollz/croc","slug":"transfer-refused-dc4a0e","errorCode":null,"errorMessage":"Transfer refused","messagePattern":"Transfer refused","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"web/src/protocol/stored.ts","lineNumber":914,"sourceCode":"  callbacks: ReceiveCallbacks;\n  signal?: AbortSignal;\n}) {\n  const { inspection, settings, callbacks, signal } = options;\n\n  if (hasVerifiedDownload(inspection.share.id)) {\n    const session: StoredReceiveSession = {\n      inspection,\n      settings,\n      callbacks,\n      signal,\n      claimToken: await claimStored(inspection, settings, signal),\n      totalBytes: inspection.offer.totalSize,\n    };\n    return commitStoredDownload(session);\n  }\n\n  const destination = await callbacks.onOffer(inspection.offer);\n  if (!destination) throw new Error(\"Transfer refused\");\n  const session: StoredReceiveSession = {\n    inspection,\n    settings,\n    callbacks,\n    signal,\n    claimToken: await claimStored(inspection, settings, signal),\n    totalBytes: 0,\n  };\n  for (\n    let fileIndex = 0;\n    fileIndex < inspection.manifest.f.length;\n    fileIndex += 1\n  ) {\n    await downloadStoredFile(session, destination, fileIndex);\n  }\n  rememberVerifiedDownload(inspection.share.id);\n  return commitStoredDownload(session);\n}","sourceCodeStart":896,"sourceCodeEnd":932,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/stored.ts#L896-L932","documentation":"Thrown by receiveStoredTransfer when the user declined: the callbacks.onOffer(inspection.offer) hook returned a falsy destination. This is the library's explicit user-refusal signal, not an infrastructure failure: the receiver saw the file list and chose not to save.","triggerScenarios":"Calling receiveStoredTransfer with an onOffer callback that returns undefined/null/false because the user clicked Cancel/Reject (or the callback has no prompt UI and implicitly returns undefined).","commonSituations":"Generic error handlers showing 'Download failed' when the user merely cancelled; automation wrappers whose onOffer forgets to return a destination path.","solutions":["Catch and branch on the 'Transfer refused' message: treat it as a clean cancel, not an error","Ensure onOffer returns the chosen destination only on explicit acceptance","Exit silently or update UI state to 'declined' on this path"],"exampleFix":"// before\ntry {\n  await receiveStoredTransfer({ inspection, settings, callbacks, signal });\n} catch {\n  showError('Download failed'); // wrong for user cancel\n}\n\n// after\ntry {\n  await receiveStoredTransfer({ inspection, settings, callbacks, signal });\n} catch (e) {\n  if (e instanceof Error && e.message === 'Transfer refused') return; // user declined\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await receiveStoredTransfer({ inspection, settings, callbacks, signal }); } catch (e) { if (e instanceof Error && e.message === 'Transfer refused') return { declined: true }; throw e; }","preventionTips":["Return a destination from onOffer only on explicit acceptance","Branch on 'Transfer refused' before generic error handling so cancel never renders as failure"],"tags":["stored-transfer","user-cancel","download","control-flow"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}