hcengineering/platform · info
Upload aborted
Error message
Upload aborted
What it means
uploadXhr checks the caller-supplied AbortSignal before creating the XMLHttpRequest and throws 'Upload aborted' immediately if signal.aborted is already true. It also rejects with the same message via xhr.onabort if the signal fires mid-upload. This is the library's cancellation mechanism, not an unexpected fault.
Source
Thrown at foundations/core/packages/storage-client/src/upload.ts:40
method: 'POST' | 'PUT'
headers: Record<string, string>
body: XMLHttpRequestBodyInit
}
/** @public */
export interface XHRUploadResult {
status: number
responseText: string
}
/** @public */
export async function uploadXhr (upload: XHRUpload, options?: FileStorageUploadOptions): Promise<XHRUploadResult> {
const signal = options?.signal
const onProgress = options?.onProgress
// Check if already aborted before starting
if (signal?.aborted === true) {
throw new Error('Upload aborted')
}
const xhr = new XMLHttpRequest()
const abortHandler = (): void => {
xhr.abort()
}
if (signal !== undefined) {
signal.addEventListener('abort', abortHandler)
}
try {
return await new Promise<XHRUploadResult>((resolve, reject) => {
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
onProgress?.({
loaded: event.loaded,View on GitHub (pinned to 63e28dc964)
Solutions
- Create a fresh AbortController for each upload instead of reusing one across calls.
- Check signal.aborted before scheduling the upload and skip it if cancellation was intended.
- Catch this error and treat it as a normal user cancellation — do not retry.
- If the upload must survive component unmount, move the controller's ownership out of the effect's cleanup scope.
Example fix
// before: shared controller reused for many files, aborted by earlier cancel
const controller = new AbortController()
for (const f of files) await uploadFile(token, ws, id, f, { signal: controller.signal })
// after: one controller per upload
for (const f of files) {
const controller = new AbortController()
await uploadFile(token, ws, id, f, { signal: controller.signal })
} Defensive patterns
Strategy: try-catch
Validate before calling
// skip the call entirely if cancellation already happened if (options?.signal?.aborted === true) return const controller = new AbortController() // fresh controller per upload
Type guard
function isUploadAborted (err: unknown): err is Error {
return err instanceof Error && err.message === 'Upload aborted'
} Try / catch
try {
await uploadFile(token, ws, uuid, file, { signal })
} catch (err) {
if (isUploadAborted(err)) return // expected user cancellation — don't retry
throw err
} Prevention
- Create one AbortController per upload; never share across uploads.
- Check signal.aborted before initiating any upload.
- In component cleanup, only abort uploads owned by that component instance.
- Treat 'Upload aborted' as success-path cancellation in UI code.
When it happens
Trigger: uploadXhr (directly or via uploadFile/uploadPromise) is called with options.signal that was already aborted, or an 'abort' event fires on the signal while the XHR is in flight.
Common situations: React/Vue effect cleanup calling AbortController.abort() on unmount while an upload is still pending; a user cancel button wired to a controller reused across uploads; awaiting an upload after its owning operation was already cancelled; reusing one signal for sequential uploads and passing an aborted one to the next.
Related errors
- Network error ${error}
- await response.text()
- Storage error ${error.error}
- Failed to initialize multipart upload
- Failed to complete multipart upload
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/da611e393649688e.
Report an issue: GitHub.