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 @useupup/core:
import {
UpupError,
UpupErrorCode,
UpupAuthError,
UpupNetworkError,
UpupValidationError,
UpupQuotaError,
UpupStorageError,
UpupConfigError,
} from '@useupup/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 '@useupup/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 and so does what recovery costs:
- The button shown after a failure is labelled "Resume" instead of "Retry".
- It invokes the same retry command as a non-resumable upload, but the file continues from its last completed part rather than restarting. The failed attempt left its server-side parts in place on purpose, and the retry re-attaches to them through
POST /multipart/resume.
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 survives a page refresh too, provided the file comes
back: pair resumable with crashRecovery and the restored file re-attaches
mid-transfer. It is on by default (persist: true) and degrades to a fresh
upload — never to a failure — whenever the session cannot be trusted. What
that costs your bucket, and the AbortIncompleteMultipartUpload lifecycle rule
it obliges you to configure, are covered in Cross-reload
resume.
In client mode (uploadEndpoint), where multipart cannot run, tus remains the
resumable option: it delegates fingerprinting and offset recovery to
tus-js-client. See Resumable Uploads.