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

# Lỗi

> Xử lý YoukaRequestError, YoukaTaskError và các lỗi có thể thử lại

SDK cung cấp hai lớp lỗi: `YoukaRequestError` cho lỗi HTTP và lỗi xác thực (validation), và `YoukaTaskError` cho các tác vụ bất đồng bộ kết thúc ở trạng thái không thành công. Cả hai đều kế thừa `Error` và mang theo các trường có cấu trúc để bạn có thể rẽ nhánh theo code, status và khả năng thử lại (retryability).

## `YoukaRequestError`

Được ném ra khi gặp lỗi HTTP, lỗi xác thực request, và phản hồi bị sai định dạng.

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

### Các trường

<ParamField path="code" type="string">
  Mã lỗi có thể đọc bởi máy, ví dụ `INVALID_REQUEST`, `UNAUTHORIZED`,
  `UPLOAD_FAILED`.
</ParamField>

<ParamField path="message" type="string">
  Mô tả dành cho con người.
</ParamField>

<ParamField path="status" type="number">
  Mã trạng thái HTTP, nếu có.
</ParamField>

<ParamField path="retryable" type="boolean">
  `true` nếu SDK coi lỗi này đáng để thử lại (giới hạn tốc độ, lỗi máy chủ tạm thời, đang phát lại theo idempotent).
</ParamField>

<ParamField path="details" type="unknown">
  Chi tiết do server cung cấp, thường là danh sách issue của Zod đối với lỗi validation.
</ParamField>

### Các code thường gặp

| Code                            | Nguyên nhân                                                            | Có thể thử lại?      |
| ------------------------------- | ---------------------------------------------------------------------- | -------------------- |
| `INVALID_REQUEST`               | Body của request không vượt qua kiểm tra schema trước khi được gửi đi. | No                   |
| `UNAUTHORIZED`                  | Thiếu hoặc API key không hợp lệ.                                       | No                   |
| `NOT_FOUND`                     | Tài nguyên không tồn tại hoặc bạn không có quyền truy cập.             | No                   |
| `CONFLICT`                      | Xung đột phiên bản (HTTP 409).                                         | Yes                  |
| `TOO_MANY_REQUESTS`             | Chạm giới hạn tốc độ (HTTP 429).                                       | Yes                  |
| `INTERNAL_SERVER_ERROR`         | Lỗi máy chủ tạm thời (HTTP 500).                                       | Yes                  |
| `IDEMPOTENT_REPLAY_IN_PROGRESS` | Request gốc vẫn đang chạy dưới cùng idempotency key (HTTP 202).        | Yes                  |
| `INVALID_RESPONSE`              | Server trả về body không khớp với schema mong đợi.                     | No                   |
| `UPLOAD_FAILED`                 | Upload qua signed URL trả về mã khác 2xx.                              | Phụ thuộc vào status |

## `YoukaTaskError`

Được ném ra từ `client.tasks.wait(...)`, `client.projects.wait(...)`, và `client.exports.wait(...)` khi tác vụ hoặc export bên dưới kết thúc ở `failed`, `cancelled`, hoặc `timed-out`.

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

### Các trường

<ParamField path="code" type="'TASK_FAILED' | 'TASK_CANCELLED' | 'TASK_TIMED_OUT'">
  Ánh xạ trực tiếp tới trạng thái kết thúc (terminal status) của tác vụ.
</ParamField>

<ParamField path="message" type="string">
  Hoặc là thông điệp lỗi của tác vụ do server cung cấp, hoặc là một thông điệp dự phòng được tạo ra.
</ParamField>

<ParamField path="status" type="TaskStatus">
  Trạng thái kết thúc của tác vụ.
</ParamField>

<ParamField path="task" type="RestTask">
  Toàn bộ payload của tác vụ tại thời điểm thất bại. Hữu ích cho việc ghi log và
  thông báo lỗi hiển thị cho người dùng.
</ParamField>

## Mẫu retry

Kết hợp `retryable` với một idempotency key để xây dựng vòng lặp retry an toàn:

```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>
  Luôn dùng lại cùng một idempotency key giữa các lần retry. Nếu không, server
  sẽ coi lần retry là một request mới và bạn có thể bị tạo trùng.
</Tip>

## Hủy (abort) và huỷ tác vụ

Khi abort một request sẽ ném ra `AbortError` tiêu chuẩn — không phải `YoukaRequestError`. Hãy kiểm tra riêng trường hợp này:

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

## Tiếp theo

* [Tasks](/vi/sdk/tasks) — các helper để chờ và polling tác vụ nâng cao
* [Authentication](/vi/sdk/authentication) — các tuỳ chọn constructor và signal
* [API errors](/vi/api/errors) — các code tương tự ở dạng HTTP thô
