{"record":{"id":"15a5c7f58b0c0330","repo":"hcengineering/platform","slug":"failed-to-reject-multipart-upload","errorCode":null,"errorMessage":"Failed to reject multipart upload","messagePattern":"Failed to reject multipart upload","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"foundations/core/packages/storage-client/src/upload.ts","lineNumber":256,"sourceCode":"  try {\n    const response = JSON.parse(result.responseText)\n    return { etag: response.etag }\n  } catch (err) {\n    throw new Error(`Failed to parse response for part ${partNumber}`)\n  }\n}\n\nasync function multipartUploadAbort (baseUrl: string, headers: Record<string, string>, uploadId: string): Promise<void> {\n  const url = new URL(concatLink(baseUrl, '/abort'))\n  url.searchParams.set('uploadId', uploadId)\n\n  const response = await fetch(url, {\n    method: 'POST',\n    headers\n  })\n\n  if (!response.ok) {\n    throw new Error('Failed to reject multipart upload')\n  }\n}\n","sourceCodeStart":238,"sourceCodeEnd":259,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/storage-client/src/upload.ts#L238-L259","documentation":"multipartUploadAbort POSTs to `<baseUrl>/abort?uploadId=...` to discard a failed multipart session; it runs in uploadMultipart's catch block whenever the upload fails or is aborted. If the abort call itself returns non-ok, it throws 'Failed to reject multipart upload'. This is a cleanup-path failure — the original upload error is what actually matters.","triggerScenarios":"uploadMultipart failed (or was aborted) and the compensating POST /abort returns non-ok: 404 (uploadId already aborted or never created), 401/403 (token expired — note the original failure may already have consumed it), 5xx from storage, or fetch-level network error rethrown by the caller.","commonSituations":"Token expired during a long upload, so both the part/complete call and the abort call fail; aborting an upload whose server-side session already timed out; transient network blip hitting exactly the cleanup request; uploadId undefined-guard passing but the session never existed.","solutions":["Wrap or patch multipartUploadAbort to log-and-swallow on cleanup: a failed abort should not mask the original upload error.","Re-authenticate before cleanup if the token expired during the upload.","Rely on server-side multipart lifecycle expiry to garbage-collect orphaned sessions if abort keeps failing.","Verify the uploadId used came from a successful multipartUploadCreate response."],"exampleFix":"// before: cleanup failure masks the real cause\n} catch (err) {\n  if (uploadId !== undefined) await multipartUploadAbort(url, headers, uploadId) // may throw 'Failed to reject...'\n  throw err\n}\n\n// after: best-effort cleanup\n} catch (err) {\n  try { if (uploadId !== undefined) await multipartUploadAbort(url, headers, uploadId) }\n  catch (abortErr) { console.warn('multipart abort failed', abortErr) }\n  throw err\n}","handlingStrategy":"try-catch","validationCode":"// only attempt abort if a session was actually created\nif (uploadId === undefined) return // nothing to abort","typeGuard":"null","tryCatchPattern":"try {\n  await multipartUploadAbort(baseUrl, headers, uploadId)\n} catch (err) {\n  // cleanup-path failure: log only, never mask the original upload error\n  console.warn('Failed to reject multipart upload', uploadId, err)\n}","preventionTips":["Always treat abort failures as non-fatal — log and continue.","Re-authenticate before cleanup if the upload failed with an auth error.","Depend on server-side session expiry to reclaim orphaned multipart uploads.","Only call abort with an uploadId returned from a successful create."],"tags":["cleanup","http","multipart","upload"],"backgroundTag":"multipart-abort-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}