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

# Errors and retries

> SquareCloudBlobError, the BlobErrorCode list, network errors that are not wrapped, and exactly which calls @squarecloud/blob retries.

## `SquareCloudBlobError`

Every API failure throws a `SquareCloudBlobError`.

| Property              | Type                      | Description                                                                                                       |
| --------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `status`              | `number`                  | HTTP status of the response.                                                                                      |
| `code`                | `BlobErrorCode`           | The API error code, e.g. `OBJECT_NOT_FOUND`.                                                                      |
| `message`             | `string`                  | The server's explanation, or the code when there is none.                                                         |
| `extra`               | `Record<string, unknown>` | Every other field of the error body, e.g. `prefix` on rule errors.                                                |
| `isUpgradeRequired()` | `() => boolean`           | `true` for `UPGRADE_REQUIRED`, the code of every plan refusal (limit, feature or plan). The `message` says which. |

```typescript theme={"system"}
import { SquareCloudBlob, SquareCloudBlobError } from "@squarecloud/blob";

const blob = new SquareCloudBlob(process.env.SQUARECLOUD_API_KEY);

try {
    await blob.shares.create(id, { password: "secret123" });
} catch (error) {
    if (error instanceof SquareCloudBlobError) {
        if (error.isUpgradeRequired()) {
            console.error(error.message); // your plan lacks this feature or limit
        } else if (error.code === "OBJECT_NOT_FOUND") {
            // ...
        } else {
            console.error(error.status, error.code, error.extra);
        }
    } else {
        throw error; // network error or file error, see below
    }
}
```

### What is not a `SquareCloudBlobError`

* **Network errors are not wrapped.** When a request gets no complete response (DNS failure, connection reset, a body cut off mid-read), the original `fetch` error is thrown as-is, after any [retries](#retry-policy).
* **File errors.** In Node.js, a `put()` path that cannot be opened throws a plain `Error` (`Cannot open file: <path>`, original error in `cause`). In a browser, a path fails with the error from importing `node:fs`.
* **A missing `@aws-sdk/client-s3`.** [`s3()`](/en/sdks/blob/s3) throws the module import error.

### `UNKNOWN_ERROR`

`UNKNOWN_ERROR` is the **only code the SDK creates itself**. It is used when the response has no error code: a non-JSON body (a proxy error page, for example) or a `2xx` response without `status: "success"`. `status` still holds the real HTTP status.

### Per-object failures

Batch operations report failures in their result instead of throwing:

* [`update()`](/en/sdks/blob/objects#updating-objects): each result has `ok: false` and a `code`.
* [`delete([ids])`](/en/sdks/blob/objects#deleting): missing objects go to `not_found`, other failures to `failed`.

## Error codes

`BlobErrorCode` is the Blob Storage API's own list of codes. It is **not** the same list as the main Square Cloud API's error codes. For each code's HTTP status and meaning, see the [Blob API errors reference](/en/blob-reference/errors).

<AccordionGroup>
  <Accordion title="Global">
    `ACCESS_DENIED`, `RATE_LIMITED`, `MISSING_SCOPE`, `RESOURCE_NOT_ALLOWED`, `UPLOAD_TOKEN_NOT_ALLOWED`, `UPLOAD_TOKEN_USED`, `PREFIX_NOT_ALLOWED`, `PERMISSION_DENIED`, `ACCOUNT_BLOCKED`, `UPGRADE_REQUIRED`, `STORAGE_QUOTA_EXCEEDED`, `PRIVATE_STORAGE_UNAVAILABLE`, `PUBLIC_STORAGE_UNAVAILABLE`, `TOO_MANY_CONCURRENT_UPLOADS`, `UPLOAD_FAILED`, `INTERNAL_SERVER_ERROR`, `NOT_FOUND`

    `RATE_LIMIT` is **deprecated**: the service no longer sends it (see `RATE_LIMITED`), but it stays in the type.
  </Accordion>

  <Accordion title="Objects">
    `OBJECT_NOT_FOUND`, `OBJECT_ALREADY_EXISTS`, `OBJECT_IS_LEGACY`, `CHECKSUM_MISMATCH`, `INVALID_CONTENT_TYPE`, `FILE_TOO_LARGE`, `FILE_TOO_SMALL`, `BLOCKED_FILE_TYPE`, `INVALID_FILE_TYPE`, `FILE_TYPE_NOT_ALLOWED`, `NOTHING_TO_UPDATE`, `VISIBILITY_CHANGE_FAILED`, `UPDATE_FAILED`, `DELETE_FAILED`, `TOO_MANY_OBJECTS`, `SAME_OBJECT`, `INVALID_DESTINATION`, `COPY_FAILED`, `PREFIX_REQUIRED`, `INVALID_CONTINUATION_TOKEN`
  </Accordion>

  <Accordion title="Multipart (chunked) uploads">
    `TOO_MANY_CONCURRENT_CHUNKS`, `TOO_MANY_OPEN_UPLOADS`, `INVALID_UPLOAD_TOKEN`, `UPLOAD_NOT_FOUND`, `NO_CHUNKS_UPLOADED`, `EMPTY_CHUNK`, `INVALID_CHUNK_PART`, `CHUNK_TOO_SMALL`, `CHUNK_TOO_LARGE`
  </Accordion>

  <Accordion title="Rules, upload tokens, shares and S3">
    `TOO_MANY_RULES`, `DUPLICATE_RULE_PREFIX`, `INVALID_RULES`, `UPLOAD_TOKEN_TOO_LARGE`, `TOO_MANY_SHARES`, `SHARE_NOT_FOUND`, `INVALID_SHARE`, `API_KEY_REQUIRED`, `LEGACY_API_KEY`
  </Accordion>

  <Accordion title="Validation (400)">
    Any `INVALID_*` code, such as `INVALID_OBJECT`, `INVALID_OBJECT_NAME` or `INVALID_RULE_PREFIX`. Rule errors carry the offending `prefix` in `error.extra`.
  </Accordion>

  <Accordion title="SDK">
    `UNKNOWN_ERROR`: the response had no error code (see [above](#unknown_error)).
  </Accordion>
</AccordionGroup>

`BlobErrorCode` also accepts any other string, so a code the service adds later still type-checks.

## Retry policy

The SDK only retries what is **safe to repeat**: `GET` calls and multipart upload **parts** (each part number can be sent again).

| Retried                                 | Calls                                                                                                                                                                                          |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Yes**, up to `maxRetries` (default 2) | `list()`, `listPage()`, `info()`, `downloadUrl()`, `stats()`, `rules.get()`, `shares.list()`, `s3Credentials()`, and each part of a multipart `put()`                                          |
| **No**, single attempt                  | Simple `put()`, starting, completing and aborting a multipart upload, `update()`, `copy()`, `move()`, `delete()`, `rules.set()`, `uploadTokens.create()`, `shares.create()`, `shares.revoke()` |

A retryable call is retried on:

* a **network error** (including a body cut off mid-read);
* **any `5xx`** response;
* `TOO_MANY_CONCURRENT_CHUNKS` on a multipart part: the server refuses the part before reading it, so it is sent again within the same budget.

It is **never** retried on any other `4xx`, **including `429`**. `RATE_LIMITED` can be an account block that lasts about 30 minutes, so the SDK leaves the decision to you.

### Backoff

Before retry `n` (starting at 0), the SDK waits:

```text theme={"system"}
min(8 s, 500 ms · 2^n) · U(0.5, 1)
```

That is, exponential backoff capped at 8 seconds, with jitter between 50 % and 100 % of the delay. With the default `maxRetries: 2`, a call makes at most 3 attempts.

### Retrying writes yourself

A write that fails with a network error or a `5xx` may or may not have been applied. Retry it only when repeating it is safe for you, for example a `put()` to the same `name` with `overwrite: true`.

```typescript theme={"system"}
import { SquareCloudBlobError } from "@squarecloud/blob";

async function putWithRetry(file, options, attempts = 3) {
    for (let i = 0; ; i++) {
        try {
            return await blob.put(file, { ...options, overwrite: true });
        } catch (error) {
            // fetch network errors are TypeErrors; 5xx are SquareCloudBlobErrors
            const retryable =
                error instanceof SquareCloudBlobError
                    ? error.status >= 500
                    : error instanceof TypeError;
            if (!retryable || i + 1 >= attempts) throw error;
            await new Promise((r) => setTimeout(r, 1000 * 2 ** i));
        }
    }
}
```

<Warning>
  Do not retry `429 RATE_LIMITED` in a tight loop: going over the account-wide budget can block the account for about 30 minutes. See the [Blob API errors reference](/en/blob-reference/errors).
</Warning>

### Timeouts

There is no client timeout and no way to cancel a call: a request lasts as long as `fetch` waits.
