Error Handling
upup reports failures as a typed UpupError (or one of its subclasses). Every
UpupError carries:
message— human-readable descriptioncode— a stable string from theUpupErrorCodeenumretryable— whether the failure is transient and worth retryingstatus— the originating HTTP status, when the error came from a response
Error types
Import the error classes and the code enum from @upupjs/core:
import {
UpupError,
UpupErrorCode,
UpupAuthError,
UpupNetworkError,
UpupValidationError,
UpupQuotaError,
UpupStorageError,
UpupConfigError,
} from '@upupjs/core'| Class | Raised when | Extra fields |
|---|---|---|
UpupError | Base class for every upup error | code, retryable, status? |
UpupValidationError | A file fails a size / type / count check | reason, file |
UpupNetworkError | A fetch / XHR fails (marked retryable) | status? |
UpupStorageError | An S3 / storage operation fails | provider, operation |
UpupAuthError | A cloud-drive OAuth / provider call fails | provider |
UpupQuotaError | A configured quota is exceeded | limit, used |
UpupConfigError | Configuration is missing or invalid (e.g. no upload target) | — |
The code values come from the UpupErrorCode enum. See Error
Codes for the complete list with the trigger
behind each one, or Error Monitoring for the
codes you'll tag most often.
Inspecting an error
Upload failures surface through the core upload-error event with the full
Error object and the file that failed. Narrow with instanceof, or switch on
code:
import { UpupError, UpupErrorCode, UpupValidationError } from '@upupjs/core'
// error is the Error object from an upload-error event (see Error Monitoring).
function describeUploadError(error: unknown): string {
if (error instanceof UpupValidationError) {
return `${error.file.name} rejected: ${error.reason}`
}
if (error instanceof UpupError) {
if (error.code === UpupErrorCode.PRESIGN_FAILED) {
return 'Your token endpoint returned an error.'
}
return error.retryable
? 'Transient failure — safe to retry.'
: 'Permanent failure — needs attention.'
}
return 'Unknown error.'
}Both surfaces — the headless core's upload-error event (full Error) and the
React onError prop (message string) — plus wiring them into an error tracker
are covered in Error Monitoring.
Retry behavior
Configure automatic retries with the maxRetries prop. Each file upload is retried up to that many times before it counts as a failure.
<UpupUploader
provider="aws"
uploadEndpoint="/api/upload-token"
maxRetries={3}
/>Once a file has exhausted its attempts and the run is marked failed, a manual "Retry" button appears in the UI. It appears whether or not maxRetries is set — that prop controls how many automatic attempts happen before the failure, not whether the manual button is offered.
Resumable upload recovery
When resumable multipart uploads are enabled (resumable={{ protocol: 'multipart' }}), the failure UI changes but the recovery mechanism does not:
- The button shown after a failure is labelled "Resume" instead of "Retry".
- It invokes the same retry command as a non-resumable upload, and the file re-uploads from the beginning. Every attempt requests a fresh multipart upload, and a failed attempt aborts the server-side upload rather than leaving its completed parts in place.
Note that this is not the "Resume" button shown while a run is paused — that one is a separate control on the pause/resume path. Only the failure-state button is described here.
Part-level continuation is not active in v3.1.0. Core ships a localStorage
multipart-session store (24-hour TTL, corruption-tolerant), but no upload
strategy calls it and the persist flag on the multipart options is never read
— so a page refresh does not resume a partially-uploaded file.
For true part-level resume, including across a page refresh, use the tus
protocol instead. It delegates fingerprinting and offset recovery to
tus-js-client, which implements them for real. See Resumable
Uploads.