{"record":{"id":"82e02362c2c5c0e4","repo":"block/buzz","slug":"upload-cancelled","errorCode":null,"errorMessage":"upload cancelled","messagePattern":"upload cancelled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"desktop/src/shared/api/tauriMedia.ts","lineNumber":31,"sourceCode":"}\n\n/** Transfer a browser File to Rust as a raw IPC body, avoiding JSON expansion. */\nexport async function uploadMediaFile(\n  file: File,\n  progressId?: string,\n  signal?: AbortSignal,\n  onDispatch?: () => void,\n): Promise<BlobDescriptor> {\n  const headers: Record<string, string> = {\n    \"x-buzz-filename\": encodeRawIpcHeader(file.name),\n  };\n  if (progressId) {\n    headers[\"x-buzz-progress-id\"] = encodeRawIpcHeader(progressId);\n  }\n\n  if (signal?.aborted) throw new Error(\"upload cancelled\");\n  const bytes = new Uint8Array(await file.arrayBuffer());\n  if (signal?.aborted) throw new Error(\"upload cancelled\");\n  onDispatch?.();\n  try {\n    return await invokeTauriRaw<BlobDescriptor>(\n      \"upload_media_bytes_raw\",\n      bytes,\n      {\n        headers,\n      },\n    );\n  } catch (error) {\n    if (error instanceof Error) throw error;\n    if (typeof error === \"string\" && error.trim()) throw new Error(error);\n    if (\n      typeof error === \"object\" &&\n      error !== null &&\n      \"message\" in error &&\n      typeof error.message === \"string\" &&\n      error.message.trim()","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/desktop/src/shared/api/tauriMedia.ts#L13-L49","documentation":"uploadMediaFile checks the AbortSignal at two points before doing work (before reading the file into bytes and before dispatching the IPC upload). If signal.aborted is already true, it throws 'upload cancelled' so no bytes are read or sent. It is an explicit cancellation path, not a failure of the upload itself.","triggerScenarios":"Caller passes an AbortSignal that was aborted before calling uploadMediaFile, or aborts between the two checks (e.g. user cancels while file.arrayBuffer() is reading).","commonSituations":"User hits Cancel in the upload UI immediately; component unmount aborts the controller; a competing action (delete attachment) aborts the in-flight upload.","solutions":["This is expected behavior on cancel — catch it and treat the upload as cancelled, not failed.","Don't abort the controller until you truly want to cancel; use a separate signal per upload.","If you never want cancellation, pass undefined for signal.","Only show an error toast when the error is not the cancellation message."],"exampleFix":"// before\ntry { await uploadMediaFile(file, { signal }); } catch (e) { showError(e); }\n// after\ntry { await uploadMediaFile(file, { signal }); }\ncatch (e) { if (!isUploadCancelled(e)) showError(e); }","handlingStrategy":"try-catch","validationCode":"if (signal?.aborted) return; // skip upload entirely, no error path needed\n","typeGuard":"function isUploadCancelled(e: unknown): boolean { return e instanceof Error && e.message === 'upload cancelled'; }\n","tryCatchPattern":"try { await uploadMediaFile(file, { signal }); } catch (e) { if (isUploadCancelled(e)) return; throw e; }\n","preventionTips":["Check signal.aborted before starting an upload","Create one AbortController per upload, cancelled only deliberately","Distinguish cancellation from failure in UI feedback"],"tags":["upload","abort-signal","cancellation"],"backgroundTag":"operation-cancelled","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-09-05T18:13:50.666Z","contentChangedAt":"2026-09-05T18:13:50.666Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}