> ## 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, retryability로 분기할 수 있도록 구조화된 필드를 담고 있습니다.

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

### Fields

<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 이슈 목록입니다.
</ParamField>

### Common codes

| Code                            | Cause                                   | Retryable?        |
| ------------------------------- | --------------------------------------- | ----------------- |
| `INVALID_REQUEST`               | 전송 전에 요청 본문이 스키마 검증에 실패했습니다.            | No                |
| `UNAUTHORIZED`                  | API 키가 없거나 유효하지 않습니다.                   | 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`              | 서버가 예상 스키마와 일치하지 않는 본문을 반환했습니다.         | No                |
| `UPLOAD_FAILED`                 | Signed URL 업로드가 2xx가 아닌 응답을 반환했습니다.     | Depends on status |

## `YoukaTaskError`

`client.tasks.wait(...)`, `client.projects.wait(...)`, `client.exports.wait(...)`에서, 기반이 되는 task 또는 export가 `failed`, `cancelled`, `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;
  }
}
```

### Fields

<ParamField path="code" type="'TASK_FAILED' | 'TASK_CANCELLED' | 'TASK_TIMED_OUT'">
  task의 종료 상태에 직접 매핑됩니다.
</ParamField>

<ParamField path="message" type="string">
  서버가 제공한 task 오류 메시지 또는 생성된 대체 메시지입니다.
</ParamField>

<ParamField path="status" type="TaskStatus">
  종료된 task 상태입니다.
</ParamField>

<ParamField path="task" type="RestTask">
  실패 시점의 전체 task 페이로드입니다. 로깅 및 사용자에게 보여줄 오류 메시지에 유용합니다.
</ParamField>

## Retry pattern

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

## Abort and cancellation

요청을 중단(abort)하면 `YoukaRequestError`가 아니라 표준 `AbortError`가 발생합니다. 명시적으로 확인하세요:

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

## What's next

* [Tasks](/ko/sdk/tasks) — wait 헬퍼와 고급 task 폴링
* [Authentication](/ko/sdk/authentication) — 생성자 옵션과 signal
* [API errors](/ko/api/errors) — 원시 HTTP에서의 동일한 코드
