> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> BlobError in TypeScript: BlobError.is(), the fields it carries, and throwing one from an upload handler.

```ts lib/avatar.ts theme={"system"}
import { BlobError, Bucket } from "@upstash/blob"

const bucket = Bucket.fromEnv()

export async function avatar(path: string) {
  try {
    return await bucket.info(path)
  } catch (e) {
    if (BlobError.is(e) && e.code === "not_found") return null
    throw e
  }
}
```

Everything the SDK throws is a `BlobError`. The same class and codes are used on the server, in the browser, and inside the React hooks. The codes, their statuses and when each one is raised are on [Errors](https://upstash.com/docs/blob/reference/errors).

`BlobError` is exported from all three entrypoints: `@upstash/blob`, `@upstash/blob/browser` and `@upstash/blob/react`.

***

## Use `BlobError.is()`, not `instanceof`

```ts theme={"system"}
if (BlobError.is(e)) {
  e.code    // BlobErrorCode
  e.status  // number
  e.message // string
}
```

> [!WARNING]
>
> An ESM copy and a CJS copy of the class are two different classes, so `instanceof` can return
> false for an error that genuinely is one. `BlobError.is()` checks a `Symbol.for` marker instead,
> which is shared across every copy of the package in the process.

`is()` is a type guard, so the fields are typed after it.

***

## Fields

```ts theme={"system"}
try {
  await bucket.del(["a.png", "b.png", "c.png"])
} catch (e) {
  if (!BlobError.is(e)) throw e
  if (e.code === "partial_delete") await queueForRetry(e.failed ?? [])
  if (e.code === "rate_limited") await sleep((e.retryAfter ?? 1) * 1000)
}
```

| Field | Type | Set by |
| - | - | - |
| `code` | `BlobErrorCode` | Always. One of the [codes](https://upstash.com/docs/blob/reference/errors#the-codes). |
| `status` | `number` | Always. |
| `message` | `string` | Always. See [Messages](https://upstash.com/docs/blob/reference/errors#messages). |
| `hint` | `string \| undefined` | Any code. See [Hints](https://upstash.com/docs/blob/reference/errors#hints-fold-into-the-message). |
| `failed` | `string[] \| undefined` | `partial_delete`: the paths that survived the delete. |
| `etag` | `string \| undefined` | `already_exists`: the etag of what is already there. |
| `size` | `number \| undefined` | `already_exists`: the size of what is already there. |
| `retryAfter` | `number \| undefined` | `mint_backoff`: seconds the credential service asked the caller to wait. On another code, only when you set it yourself. |
| `cause` | `unknown` | The underlying error, when there was one. Standard `Error.cause`. |

`BlobErrorCode` and `BlobErrorOptions` are exported as types from `@upstash/blob`. `BlobErrorCode` is also exported from `/browser` and `/react`.

### Built-in hints

```ts theme={"system"}
new BlobError("length_required").message
// 'Length required (pass { size } or { maxSize } so the length is known before the first byte)'
```

| 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](https://upstash.com/docs/blob/reference/errors#hints-fold-into-the-message).

***

## Throwing one

```ts theme={"system"}
new BlobError("not_found").message                         // 'Not found'
new BlobError("forbidden", "not your thread").message       // 'Not your thread'
new BlobError("too_large", { message: "you are out of space" })
```

`new BlobError(code, options?)` takes a message string, or `{ message, hint, status, failed, etag, size, retryAfter, cause }`. Throw one from an upload handler callback to refuse an upload with that code; see [Refusing](https://upstash.com/docs/blob/uploads/upload-handler#refusing).

***

## Bad options are a `TypeError`

An unparseable `'5mbit'`, a missing `token`, a route with no `onBeforeUpload`: those throw a `TypeError` where the option is written, not a `BlobError` per request. So does a path with a `.` or `..` segment.

An `onBeforeUpload` that returns `constraints` wider than the route's also throws a `TypeError`, while the request runs. It is not a `BlobError`, so the request ends as a 500 rather than a refusal the browser can read. See [Narrowing per user](https://upstash.com/docs/blob/uploads/constraints#narrowing-per-user).

***

## Cancel is an `AbortError`

```ts theme={"system"}
const record = start({ file })
record?.cancel()          // status becomes 'canceled', error stays undefined
```

A canceled browser upload rejects with an `AbortError`, not a `BlobError`. See [Errors the browser raises on its own](https://upstash.com/docs/blob/reference/errors#errors-the-browser-raises-on-its-own).


## Related topics

- [Errors](https://upstash.com/docs/blob/sdks/py/errors.md)
- [Error read ECONNRESET](https://upstash.com/docs/redis/troubleshooting/econn_reset.md)
- [Migration Guide](https://upstash.com/docs/workflow/howto/migrations.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.