Skip to main content
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.
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

code
string
Machine-readable error code, for example INVALID_REQUEST, UNAUTHORIZED, UPLOAD_FAILED.
message
string
Human-readable description.
status
number
HTTP status code, if available.
retryable
boolean
true if the SDK considers the error worth retrying (rate limits, transient server errors, idempotent replay in progress).
details
unknown
Server-provided details, typically a Zod issue list for validation errors.

Common codes

CodeCauseRetryable?
INVALID_REQUESTRequest body failed schema validation before being sent.No
UNAUTHORIZEDMissing or invalid API key.No
NOT_FOUNDResource doesn’t exist or you lack access.No
CONFLICTVersion conflict (HTTP 409).Yes
TOO_MANY_REQUESTSRate limit hit (HTTP 429).Yes
INTERNAL_SERVER_ERRORTransient server failure (HTTP 500).Yes
IDEMPOTENT_REPLAY_IN_PROGRESSThe original request is still running under the same idempotency key (HTTP 202).Yes
INVALID_RESPONSEServer returned a body that didn’t match the expected schema.No
UPLOAD_FAILEDSigned 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.
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

code
'TASK_FAILED' | 'TASK_CANCELLED' | 'TASK_TIMED_OUT'
Maps directly to the task’s terminal status.
message
string
Either the server-provided task error message or a generated fallback.
status
TaskStatus
The terminal task status.
task
RestTask
The full task payload at the moment of failure. Useful for logging and user-facing error messages.

Retry pattern

Combine retryable with an idempotency key to build a safe retry loop:
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",
  }),
);
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.

Abort and cancellation

Aborting a request throws a standard AbortError — not a YoukaRequestError. Check for it explicitly:
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