{"record":{"id":"b47c3b312cae5c65","repo":"transloadit/uppy","slug":"c-error-prefix-failed-to-abort-multipart-upload","errorCode":null,"errorMessage":"${C.ERROR_PREFIX}Failed to abort multipart upload: ${String(parsed.error.message)}","messagePattern":"(.+?)Failed to abort multipart upload: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@uppy/aws-s3/src/s3-client/S3mini.ts","lineNumber":499,"sourceCode":"    this._checkKey(key)\n    if (!uploadId) {\n      throw new TypeError(C.ERROR_UPLOAD_ID_REQUIRED)\n    }\n\n    const { xhr } = await this.request({\n      request: { method: 'DELETE', key, uploadId },\n      signal,\n    })\n\n    const parsed = U.parseXml(xhr.responseText) as Record<string, unknown>\n    if (\n      parsed &&\n      'error' in parsed &&\n      typeof parsed.error === 'object' &&\n      parsed.error !== null &&\n      'message' in parsed.error\n    ) {\n      throw new Error(\n        `${C.ERROR_PREFIX}Failed to abort multipart upload: ${String(\n          parsed.error.message,\n        )}`,\n      )\n    }\n  }\n\n  private _buildCompleteMultipartUploadXml(\n    parts: Array<IT.UploadPart>,\n  ): string {\n    let xml = '<CompleteMultipartUpload>'\n    for (const part of parts) {\n      xml += `<Part><PartNumber>${part.partNumber}</PartNumber><ETag>${part.etag}</ETag></Part>`\n    }\n    xml += '</CompleteMultipartUpload>'\n    return xml\n  }\n","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/s3-client/S3mini.ts#L481-L517","documentation":"abortMultipartUpload sent a DELETE but the response body parsed to an S3-style <Error> payload with a message, meaning S3 refused the abort (e.g. the upload no longer exists).","triggerScenarios":"Aborting an uploadId that was already completed or aborted (NoSuchUpload), or aborting a key/uploadId pair that never existed.","commonSituations":"Double abort attempts, retry logic aborting after a successful complete, stale persisted state after server restarts.","solutions":["If the parsed error is NoSuchUpload, treat the abort as already done and ignore it","Ensure abort is only called once per upload (idempotent wrapper)","Check that the same key and uploadId from creation are used"],"exampleFix":"// before\nawait s3Mini.abortMultipartUpload({ key, uploadId })\n\n// after\ntry {\n  await s3Mini.abortMultipartUpload({ key, uploadId })\n} catch (err) {\n  if (!String(err.message).includes('NoSuchUpload')) throw err\n}","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { await abort(...) } catch (e) { if (String(e?.message).includes('NoSuchUpload')) return; throw e }","preventionTips":["Only abort uploads you created this session","Avoid double-abort in cleanup paths","Match key and uploadId from creation"],"tags":["s3","multipart","abort"],"backgroundTag":"s3-multipart-abort-failed","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}