> ## 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.

# 错误和重试

> SquareCloudBlobError、BlobErrorCode 列表、不会被包装的网络错误，以及 @squarecloud/blob 究竟会重试哪些调用。

## `SquareCloudBlobError`

每个 API 失败都会抛出 `SquareCloudBlobError`。

| 属性                    | 类型                        | 说明                                                                      |
| --------------------- | ------------------------- | ----------------------------------------------------------------------- |
| `status`              | `number`                  | 响应的 HTTP 状态。                                                            |
| `code`                | `BlobErrorCode`           | API 错误代码，例如 `OBJECT_NOT_FOUND`。                                         |
| `message`             | `string`                  | 服务器给出的说明，没有说明时为该代码。                                                     |
| `extra`               | `Record<string, unknown>` | 错误响应体中的其他所有字段，例如规则错误中的 `prefix`。                                        |
| `isUpgradeRequired()` | `() => boolean`           | 对于 `UPGRADE_REQUIRED` 为 `true`，这是所有套餐拒绝（限制、功能或套餐）的代码。`message` 会说明具体原因。 |

```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
    }
}
```

### 哪些不是 `SquareCloudBlobError`

* **网络错误不会被包装。** 当请求没有得到完整的响应时（DNS 失败、连接重置、响应体在读取中途被截断），原始的 `fetch` 错误会在所有[重试](#重试策略)之后被原样抛出。
* **文件错误。** 在 Node.js 中，无法打开的 `put()` 路径会抛出普通的 `Error`（`Cannot open file: <path>`，原始错误位于 `cause` 中）。在浏览器中，路径会以导入 `node:fs` 时的错误失败。
* **缺少 `@aws-sdk/client-s3`。** [`s3()`](/zh/sdks/blob/s3) 会抛出模块导入错误。

### `UNKNOWN_ERROR`

`UNKNOWN_ERROR` 是 **SDK 自己创建的唯一代码**。当响应没有错误代码时使用：非 JSON 的响应体（例如代理的错误页面），或不带 `status: "success"` 的 `2xx` 响应。`status` 仍保存真实的 HTTP 状态。

### 单个对象的失败

批量操作会在结果中报告失败，而不是抛出异常：

* [`update()`](/zh/sdks/blob/objects#更新对象)：每个结果都有 `ok: false` 和一个 `code`。
* [`delete([ids])`](/zh/sdks/blob/objects#删除)：不存在的对象进入 `not_found`，其他失败进入 `failed`。

## 错误代码

`BlobErrorCode` 是 Blob Storage API 自身的代码列表。它与 Square Cloud 主 API 的错误代码**不是**同一个列表。关于每个代码的 HTTP 状态和含义，请参见 [Blob API 错误参考](/zh/blob-reference/errors)。

<AccordionGroup>
  <Accordion title="全局">
    `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` **已弃用**：服务不再发送它（参见 `RATE_LIMITED`），但它仍保留在该类型中。
  </Accordion>

  <Accordion title="对象">
    `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="分片（分块）上传">
    `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="规则、上传令牌、分享和 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="验证（400）">
    任何 `INVALID_*` 代码，例如 `INVALID_OBJECT`、`INVALID_OBJECT_NAME` 或 `INVALID_RULE_PREFIX`。规则错误会在 `error.extra` 中携带出错的 `prefix`。
  </Accordion>

  <Accordion title="SDK">
    `UNKNOWN_ERROR`：响应没有错误代码（参见[上文](#unknown_error)）。
  </Accordion>
</AccordionGroup>

`BlobErrorCode` 也接受任何其他字符串，因此服务日后新增的代码仍能通过类型检查。

## 重试策略

SDK 只重试**可以安全重复**的操作：`GET` 调用和分片上传的**分片**（每个分片编号都可以再次发送）。

| 是否重试                          | 调用                                                                                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **是**，最多 `maxRetries` 次（默认 2） | `list()`、`listPage()`、`info()`、`downloadUrl()`、`stats()`、`rules.get()`、`shares.list()`、`s3Credentials()`，以及分片 `put()` 的每个分片                |
| **否**，仅一次尝试                   | 简单 `put()`，分片上传的启动、完成和中止，`update()`、`copy()`、`move()`、`delete()`、`rules.set()`、`uploadTokens.create()`、`shares.create()`、`shares.revoke()` |

可重试的调用会在以下情况下重试：

* **网络错误**（包括响应体在读取中途被截断）；
* **任何 `5xx`** 响应；
* 分片上传的某个分片返回 `TOO_MANY_CONCURRENT_CHUNKS`：服务器在读取该分片之前就拒绝了它，因此会在同一预算内再次发送。

对于其他任何 `4xx`，**包括 `429`**，**永远不会**重试。`RATE_LIMITED` 可能是持续约 30 分钟的账户封锁，因此 SDK 将决定权留给你。

### 退避

在第 `n` 次重试（从 0 开始）之前，SDK 会等待：

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

也就是说，这是上限为 8 秒的指数退避，抖动在延迟的 50% 到 100% 之间。使用默认的 `maxRetries: 2` 时，一次调用最多进行 3 次尝试。

### 自行重试写操作

因网络错误或 `5xx` 失败的写操作可能已被应用，也可能没有。仅当重复执行对你而言是安全的时候才重试，例如对同一 `name` 使用 `overwrite: true` 的 `put()`。

```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>
  不要在紧密循环中重试 `429 RATE_LIMITED`：超出账户范围的预算可能会导致账户被封锁约 30 分钟。参见 [Blob API 错误参考](/zh/blob-reference/errors)。
</Warning>

### 超时

没有客户端超时，也无法取消调用：请求会持续到 `fetch` 等待结束为止。
