Every failure the SDK reports on purpose is a *blob.Error. Its codes are the ones the TypeScript SDK and the browser client use, so an error returned from your Go handler reaches a React hook with its code intact. The codes, their statuses and when each one is returned are on Errors.
Matching it#
Three ways to ask, all of which look through wrapped errors:
errors.As(err, &be)gives you the*blob.Errorand its fields.errors.Is(err, blob.ErrNotFound)compares the code only. The sentinels areErrNotFound,ErrAlreadyExists,ErrConflict,ErrTooLarge,ErrInvalidInput,ErrUnauthorized,ErrForbiddenandErrContentTypeDenied.blob.HasCode(err, code)works for every code, including the ones without a sentinel.
ErrorCode is a string type, and every code has a constant: CodeNotFound, CodePartialDelete, CodeMintBackoff, and so on.
A canceled or expired ctx is returned as the context’s own error, not a *blob.Error, so errors.Is(err, context.Canceled) finds it.
Fields#
| Field | Type | Set by |
|---|---|---|
Code | ErrorCode | Always. One of the codes. |
Status | int | Always. |
Message | string | Always. See Messages. |
Hint | string | Any code. See Hints. |
Failed | []string | partial_delete: the paths that survived the delete. |
ETag | string | already_exists: the etag of what is already there. |
Size | *int64 | already_exists: the size of what is already there. |
RetryAfter | time.Duration | mint_backoff: how long the credential service asked the caller to wait. On another code, only when you set it yourself. |
The underlying error, when there was one, is the wrapped cause that errors.Unwrap returns: a network error, storage’s own message, or the reason Upstash refused to sign for a suspended or read-only bucket. A move_left_a_copy wraps the failed delete. The cause is not sent in the JSON, except that an error from an upload handler’s Input becomes the message.
Built-in hints#
| Code | Built-in hint |
|---|---|
signature_mismatch | the upload did not match what was authorized, usually a different size or content type |
length_required | pass Size or MaxSize so the length is known before the first byte |
How a hint folds into the message is on Hints fold into the message.
Returning one#
NewError(code, ErrorOptions) builds one the way the SDK does: the code’s default message and hint, the message sentence-cased with the hint folded in, and the code’s status unless Status is set. ErrorOptions has Message, Hint, Status, Failed, ETag, Size, RetryAfter and Cause. Return one from an upload handler callback to refuse an upload with that code; see Refusing.
blob.ErrorFromStatus(status, opts) picks the code for a bare HTTP status, through the table on What reaches the browser, falling back to request_failed.
Answering with one#
A *blob.Error encodes to the JSON body an upload route answers a refusal with, and what the browser client rebuilds into its own BlobError. hint, failed, etag, size and retryAfter (in seconds) are included when they are set. The cause never is.
The handler writes this body for you. Write it yourself to answer with a *blob.Error from a route of your own, such as one that calls Put for useServerUpload:
Bad options are returned where they are written#
A mistake comes back as an error from the call that takes the option, and the kind of error says when to expect it:
| Mistake | Returns |
|---|---|
NewBucketFromEnv() with the variable unset, an empty or malformed token, Retry.Retries below 0 | A plain error, from the constructor |
Cache: "forever" on Options | invalid_input, from the constructor |
An upload handler with no OnBeforeUpload, an empty non-nil Routes, a bad route name, a negative MaxSize, no token | A plain error, from NewUploadHandler |
An empty path, or one with a . or .. segment, on any Bucket method | invalid_input |
A negative Size, MaxSize, ExpiresIn or olderThan, a bad Cache on a call | invalid_input |
blob.ParseSize("5mbit") | invalid_input |
A ContentTypes list that is empty, or holds an entry that is not a type/subtype or one of image/*, video/* and audio/* | invalid_content_type_pattern, on a Put or from NewUploadHandler. On an UploadRoute, an empty list clears the handler’s instead. |
A ContentType that is not a media type, on a signed URL | invalid_input |
The plain errors start with blob: and are meant to stop your program at startup. NewUploadHandler wraps a *blob.Error from its options, so errors.As still finds it.
A few handler mistakes only show up while a request runs. They are logged on the handler’s Logger and answered as a 500:
OnBeforeUploadreturned an emptyPath, a negativeMaxSize, or aStatethatencoding/jsoncannot encode.OnBeforeUploadreturnedContentTypesor aMaxSizewider than the route’s; see Narrowing per user.OnBeforeUploadreturnedContentTypesthat is a non-nil empty slice or holds a bad entry. This one is answered as a 500invalid_content_type_patternand not logged. Leave the field nil to keep the route’s list.OnUploadCompletereturned a value thatencoding/jsoncannot encode. The object is kept.