{"record":{"id":"e1a2f93af30c4093","repo":"schollz/croc","slug":"aborterror","errorCode":"AbortError","errorMessage":"Destination selection cancelled","messagePattern":"Destination selection cancelled","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"info","filePath":"web/src/protocol/storage.ts","lineNumber":362,"sourceCode":"      window.location.hostname === \"localhost\" ||\n      window.location.hostname === \"127.0.0.1\")\n  );\n}\n\nexport async function chooseReceiveDestination(offer: TransferOffer) {\n  if (!window.showDirectoryPicker) return new DownloadDestination();\n  const directory = await window.showDirectoryPicker({ mode: \"readwrite\" });\n  const collisions: string[] = [];\n  for (const file of offer.files) {\n    if (await fileExists(directory, file)) collisions.push(file.path);\n  }\n  if (\n    collisions.length > 0 &&\n    !window.confirm(\n      `${collisions.length} file${collisions.length === 1 ? \"\" : \"s\"} already exist in that folder. Replace them?`,\n    )\n  ) {\n    throw new DOMException(\"Destination selection cancelled\", \"AbortError\");\n  }\n  return new DirectoryDestination(directory);\n}\n\nexport async function chooseStoredReceiveDestination(offer: TransferOffer) {\n  if (window.showDirectoryPicker) {\n    return chooseReceiveDestination(offer);\n  }\n  if (supportsStreamingDownloadDestination()) {\n    return new StreamingDownloadDestination();\n  }\n  const largest = offer.files.reduce(\n    (maximum, file) => Math.max(maximum, file.size),\n    0,\n  );\n  if (largest > 256 * 1024 * 1024) {\n    throw new Error(\n      \"This browser cannot stream a file this large. Receive it with the croc CLI instead.\",","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/storage.ts#L344-L380","documentation":"Thrown as a DOMException with name AbortError by chooseReceiveDestination after the user was shown a window.confirm overwrite dialog for existing files in the picked directory and declined to replace them. It signals deliberate user cancellation of destination selection, not a system failure. Existing-file detection is done per offer file via fileExists() against the picked directory handle.","triggerScenarios":"window.showDirectoryPicker({mode:\"readwrite\"}) succeeds, one or more offered file paths already exist in the chosen folder, and the user clicks Cancel on the \"N file(s) already exist in that folder. Replace them?\" confirm dialog.","commonSituations":"Re-running a receive into the same folder after a partial earlier transfer; receivers who deliberately do not want to clobber existing data. Note the directory picker's own Cancel produces a different AbortError from showDirectoryPicker itself, so catch code must handle both origins gracefully.","solutions":["Catch DOMException with name === \"AbortError\" around chooseReceiveDestination/chooseStoredReceiveDestination and treat it as a benign user cancel (stop quietly or re-prompt)","If the user should proceed, instruct them to confirm the replace dialog or pick a different/empty folder","Pre-clean or rename colliding files in the target folder before re-running the receive"],"exampleFix":"// before\nconst destination = await chooseReceiveDestination(offer);\n\n// after\ntry {\n  const destination = await chooseReceiveDestination(offer);\n} catch (error) {\n  if (error instanceof DOMException && error.name === \"AbortError\") return; // user cancelled\n  throw error;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isUserCancel = (e: unknown): boolean => e instanceof DOMException && e.name === \"AbortError\";","tryCatchPattern":"try { destination = await chooseStoredReceiveDestination(offer); } catch (e) { if (e instanceof DOMException && e.name === \"AbortError\") { showCancelledState(); return; } throw e; }","preventionTips":["Treat AbortError from destination selection as a first-class UI state (cancelled), not an error toast","Remember it can come from either the directory picker itself or the overwrite confirm — handle both the same way"],"tags":["user-cancel","abort","file-system-access","browser"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}