{"record":{"id":"693268a281e4dc19","repo":"gildas-lormeau/SingleFile","slug":"upload-cancelled-693268","errorCode":"upload_cancelled","errorMessage":"upload_cancelled","messagePattern":"upload_cancelled","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/lib/gdrive/gdrive.js","lineNumber":464,"sourceCode":"\t\t\t\"Authorization\": \"Bearer \" + mediaUploader.token,\n\t\t\t\"Content-Type\": mediaUploader.contentType,\n\t\t\t\"Content-Range\": \"bytes \" + mediaUploader.offset + \"-\" + (end - 1) + \"/\" + mediaUploader.file.size,\n\t\t\t\"X-Upload-Content-Type\": mediaUploader.contentType\n\t\t},\n\t\tbody: content\n\t});\n\tif (mediaUploader.onProgress && !mediaUploader.cancelled) {\n\t\tmediaUploader.onProgress(mediaUploader.offset + mediaUploader.chunkSize, mediaUploader.file.size);\n\t}\n\tif (httpResponse.status == 200 || httpResponse.status == 201) {\n\t\treturn httpResponse.json();\n\t} else if (httpResponse.status == 308) {\n\t\tconst range = httpResponse.headers.get(\"Range\");\n\t\tif (range) {\n\t\t\tmediaUploader.offset = parseInt(range.match(/\\d+/g).pop(), 10) + 1;\n\t\t}\n\t\tif (mediaUploader.cancelled) {\n\t\t\tthrow new Error(\"upload_cancelled\");\n\t\t} else {\n\t\t\treturn sendFile(mediaUploader);\n\t\t}\n\t} else {\n\t\tgetResponse(httpResponse);\n\t}\n}\n\nasync function getJSON(httpResponse) {\n\thttpResponse = getResponse(httpResponse);\n\tconst response = await httpResponse.json();\n\tif (response.error) {\n\t\tthrow new Error(response.error);\n\t} else {\n\t\treturn response;\n\t}\n}\n","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/lib/gdrive/gdrive.js#L446-L482","documentation":"sendFile throws \"upload_cancelled\" during a resumable (chunked) upload when the Drive API responds with HTTP 308 (Resume Incomplete) and the mediaUploader has been marked cancelled. Instead of recursing to send the next chunk, the upload is aborted with this error so callers can distinguish an intentional cancellation from a network failure.","triggerScenarios":"Calling the cancel() method on the uploader (or setting mediaUploader.cancelled = true) while a multi-chunk upload is in progress; the next 308 response from the resumable endpoint then observes cancelled === true and throws.","commonSituations":"User presses a Cancel button during a large file upload; app cancels uploads on navigation; timeouts that cancel in-flight uploads; testing partial-upload recovery logic.","solutions":["If cancellation was unintentional, do not set cancelled before the upload finishes; restart the upload from scratch.","Resume instead of cancelling: create a new uploader positioned at mediaUploader.offset (derived from the 308 Range header) and call sendFile again.","Handle the error in UI code by showing the upload as cancelled and offering a retry.","Check that no shared uploader instance is being cancelled by another concurrent request."],"exampleFix":"// before\nuploader.cancel(); // later: Error: upload_cancelled\n// after\n// don't cancel; resume from server-reported offset\nconst offset = uploader.offset;\nuploader.startAt(offset);\nawait sendFile(uploader);","handlingStrategy":"try-catch","validationCode":"// before starting: ensure no cancel is pending\nif (uploader.cancelled) throw new Error(\"start aborted: uploader already cancelled\");","typeGuard":null,"tryCatchPattern":"try { await upload(mediaUploader); }\ncatch (e) {\n  if (e.message === \"upload_cancelled\") { markUploadCancelledInUI(mediaUploader.name); return; }\n  throw e;\n}","preventionTips":["Only cancel uploads from explicit user actions","Resume from the offset in the 308 Range header instead of restarting from zero","Disable the Cancel button once the final chunk has been sent"],"tags":["upload","cancelled","resumable-upload","gdrive"],"backgroundTag":"upload-cancelled-by-user","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}