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

# Errors

> Handle YoukaRequestError, YoukaTaskError, and retryable failures

The SDK surfaces two error classes: `YoukaRequestError` for HTTP and validation failures, and `YoukaTaskError` for async tasks that ended in a non-successful state. Both extend `Error` and carry structured fields so you can branch on code, status, and retryability.

## `YoukaRequestError`

Thrown for HTTP errors, request validation failures, and malformed responses.

```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">
  Machine-readable error code, for example `INVALID_REQUEST`, `UNAUTHORIZED`,
  `UPLOAD_FAILED`.
</ParamField>

<ParamField path="message" type="string">
  Human-readable description.
</ParamField>

<ParamField path="status" type="number">
  HTTP status code, if available.
</ParamField>

<ParamField path="retryable" type="boolean">
  `true` if the SDK considers the error worth retrying (rate limits, transient
  server errors, idempotent replay in progress).
</ParamField>

<ParamField path="details" type="unknown">
  Server-provided details, typically a Zod issue list for validation errors.
</ParamField>

### Common codes

| Code                            | Cause                                                                            | Retryable?        |
| ------------------------------- | -------------------------------------------------------------------------------- | ----------------- |
| `INVALID_REQUEST`               | Request body failed schema validation before being sent.                         | No                |
| `UNAUTHORIZED`                  | Missing or invalid API key.                                                      | No                |
| `NOT_FOUND`                     | Resource doesn't exist or you lack access.                                       | No                |
| `CONFLICT`                      | Version conflict (HTTP 409).                                                     | Yes               |
| `TOO_MANY_REQUESTS`             | Rate limit hit (HTTP 429).                                                       | Yes               |
| `INTERNAL_SERVER_ERROR`         | Transient server failure (HTTP 500).                                             | Yes               |
| `IDEMPOTENT_REPLAY_IN_PROGRESS` | The original request is still running under the same idempotency key (HTTP 202). | Yes               |
| `INVALID_RESPONSE`              | Server returned a body that didn't match the expected schema.                    | No                |
| `UPLOAD_FAILED`                 | Signed URL upload returned non-2xx.                                              | Depends on status |

## `YoukaTaskError`

Thrown from `client.tasks.wait(...)`, `client.projects.wait(...)`, and `client.exports.wait(...)` when the underlying task or export ends in `failed`, `cancelled`, or `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'">
  Maps directly to the task's terminal status.
</ParamField>

<ParamField path="message" type="string">
  Either the server-provided task error message or a generated fallback.
</ParamField>

<ParamField path="status" type="TaskStatus">
  The terminal task status.
</ParamField>

<ParamField path="task" type="RestTask">
  The full task payload at the moment of failure. Useful for logging and
  user-facing error messages.
</ParamField>

## Retry pattern

Combine `retryable` with an idempotency key to build a safe retry loop:

```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>
  Always reuse the same idempotency key across retries. Otherwise the server
  treats the retry as a new request and you may end up with duplicates.
</Tip>

## Abort and cancellation

Aborting a request throws a standard `AbortError` — not a `YoukaRequestError`. Check for it explicitly:

```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](/en/sdk/tasks) — wait helpers and advanced task polling
* [Authentication](/en/sdk/authentication) — constructor options and signals
* [API errors](/en/api/errors) — the same codes in raw HTTP
