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

# 错误

> 处理 YoukaRequestError、YoukaTaskError 以及可重试的失败

SDK 提供两种错误类：用于 HTTP 与校验失败的 `YoukaRequestError`，以及用于以非成功状态结束的异步任务的 `YoukaTaskError`。两者都继承自 `Error`，并携带结构化字段，便于你根据 code、status 和是否可重试进行分支处理。

## `YoukaRequestError`

在发生 HTTP 错误、请求校验失败或响应格式不正确时抛出。

```ts theme={null}
import { YoukaRequestError } from "@youka/sdk";

try {
  await client.projects.create(body);
} catch (error) {
  if (error instanceof YoukaRequestError) {
    console.error({
      code: error.code,
      message: error.message,
      status: error.status,
      retryable: error.retryable,
      details: error.details,
    });
  } else {
    throw error;
  }
}
```

### 字段

<ParamField path="code" type="string">
  机器可读的错误码，例如 `INVALID_REQUEST`、`UNAUTHORIZED`、`UPLOAD_FAILED`。
</ParamField>

<ParamField path="message" type="string">
  面向人的错误描述。
</ParamField>

<ParamField path="status" type="number">
  HTTP 状态码（如果可用）。
</ParamField>

<ParamField path="retryable" type="boolean">
  当 SDK 认为该错误值得重试时为 `true`（例如触发限流、短暂的服务器错误、幂等重放仍在进行中）。
</ParamField>

<ParamField path="details" type="unknown">
  由服务器提供的详情；对于校验错误，通常是 Zod 的 issue 列表。
</ParamField>

### 常见 code

| Code                            | 原因                           | 可重试？       |
| ------------------------------- | ---------------------------- | ---------- |
| `INVALID_REQUEST`               | 请求体在发送前未通过 schema 校验。        | No         |
| `UNAUTHORIZED`                  | 缺少或无效的 API key。              | No         |
| `NOT_FOUND`                     | 资源不存在或你没有访问权限。               | No         |
| `CONFLICT`                      | 版本冲突（HTTP 409）。              | Yes        |
| `TOO_MANY_REQUESTS`             | 触发速率限制（HTTP 429）。            | Yes        |
| `INTERNAL_SERVER_ERROR`         | 短暂的服务器故障（HTTP 500）。          | Yes        |
| `IDEMPOTENT_REPLAY_IN_PROGRESS` | 原始请求仍在使用相同的幂等键运行中（HTTP 202）。 | Yes        |
| `INVALID_RESPONSE`              | 服务器返回的 body 与预期 schema 不匹配。  | No         |
| `UPLOAD_FAILED`                 | Signed URL 上传返回非 2xx。        | 取决于 status |

## `YoukaTaskError`

当底层 task 或 export 以 `failed`、`cancelled` 或 `timed-out` 结束时，由 `client.tasks.wait(...)`、`client.projects.wait(...)` 和 `client.exports.wait(...)` 抛出。

```ts theme={null}
import { YoukaTaskError } from "@youka/sdk";

try {
  await client.projects.wait(created);
} catch (error) {
  if (error instanceof YoukaTaskError) {
    console.error({
      code: error.code,
      message: error.message,
      status: error.status,
      task: error.task,
    });
  } else {
    throw error;
  }
}
```

### 字段

<ParamField path="code" type="'TASK_FAILED' | 'TASK_CANCELLED' | 'TASK_TIMED_OUT'">
  直接映射到任务的终态状态。
</ParamField>

<ParamField path="message" type="string">
  服务器提供的任务错误信息，或生成的兜底信息。
</ParamField>

<ParamField path="status" type="TaskStatus">
  任务的终态状态。
</ParamField>

<ParamField path="task" type="RestTask">
  失败时刻的完整 task payload。适用于日志记录与面向用户的错误提示。
</ParamField>

## 重试模式

将 `retryable` 与幂等键结合，构建一个安全的重试循环：

```ts theme={null}
import { YoukaRequestError } from "@youka/sdk";

async function createWithRetry<T>(fn: () => Promise<T>, maxAttempts = 3) {
  let lastError: unknown;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (
        error instanceof YoukaRequestError &&
        error.retryable &&
        attempt < maxAttempts
      ) {
        await new Promise((r) => setTimeout(r, 2 ** attempt * 1_000));
        continue;
      }
      throw error;
    }
  }

  throw lastError;
}

await createWithRetry(() =>
  client.projects.create(body, {
    idempotencyKey: "import-2026-04-08-song-001",
  }),
);
```

<Tip>
  重试时务必复用同一个幂等键。否则服务器会将重试视为一次新请求，你可能会得到重复结果。
</Tip>

## 中止与取消

中止请求会抛出标准 `AbortError` —— 而不是 `YoukaRequestError`。请显式检查它：

```ts theme={null}
try {
  await client.projects.wait(created, { signal: controller.signal });
} catch (error) {
  if (error instanceof Error && error.name === "AbortError") {
    console.log("Cancelled by user");
    return;
  }
  throw error;
}
```

## 下一步

* [Tasks](/zh/sdk/tasks) — wait 辅助方法与高级任务轮询
* [Authentication](/zh/sdk/authentication) — 构造函数选项与 signals
* [API errors](/zh/api/errors) — 原始 HTTP 中的相同 code
