Files
skills/docs/media/audio/third-party-elevenlabs/src/errors/errors.md
2026-05-29 08:20:56 +01:00

5.4 KiB

errors

Stable error class hierarchy and code registry for @suaveplan/third-party-elevenlabs. Every non-2xx HTTP response and every transport-level failure maps to one of 17 typed ElevenLabsApiError subclasses under namespace THIRD_PARTY_ELEVENLABS.*.

(no submodules)

Purpose

The ElevenLabs API surface is large and surfaces failures across HTTP status codes, malformed JSON bodies, transport timeouts, and WebSocket protocol errors. Without a stable error hierarchy, every consumer of this client would have to learn the upstream's quirks. This module defines the canonical 17-code surface so callers can branch on error.code or error instanceof RateLimitExceededError without touching the wire.

The mapping is total and disjoint over the [400, 600) HTTP range, every documented network failure mode, every JSON / Zod parse failure, and the two WebSocket failure modes (handshake / protocol). Every error preserves the original cause via the cause-chain on SuaveplanError.

Features

  • 17 error codes registered via defineCodes('THIRD_PARTY_ELEVENLABS', ...).
  • 17 typed subclasses, one per code, with sensible default messages.
  • Convenience options surface common transport context:
    • RateLimitExceededError({ retryAfterMs }) — retries planning.
    • RequestValidationFailedError({ body }) — preserved 422 body.
    • WebSocketConnectionFailedError({ code }) — close code.
    • NetworkTimeoutError({ timeoutMs }) — observed limit.
    • Every HTTP error accepts { endpoint, statusCode, requestId }.
  • Caller-supplied context is shallow-merged on top of derived context so adapter packages can layer their own attribution (e.g. tenantId).

Usage: Basic Example

import {
  ElevenLabsApiError,
  RateLimitExceededError,
} from "@suaveplan/third-party-elevenlabs/errors/errors";

try {
  await client.fetchJson(req, schema);
} catch (err) {
  if (err instanceof RateLimitExceededError) {
    console.log(`retry after ${err.context?.retryAfterMs ?? "unknown"} ms`);
  }
}

Usage: Advanced Example

Branch on every documented failure mode and re-throw with adapter context:

import {
  ElevenLabsApiError,
  ELEVENLABS_CODES,
} from "@suaveplan/third-party-elevenlabs/errors/errors";

function handleFailure(err: unknown): never {
  if (!(err instanceof ElevenLabsApiError)) throw err;
  switch (err.code) {
    case ELEVENLABS_CODES["AUTH.UNAUTHORIZED"]:
    case ELEVENLABS_CODES["AUTH.FORBIDDEN"]:
      throw new ProviderNotConfiguredError({ cause: err });
    case ELEVENLABS_CODES["RATE_LIMIT.EXCEEDED"]:
    case ELEVENLABS_CODES["RATE_LIMIT.QUOTA_EXHAUSTED"]:
      throw new ProviderRateLimitedError({
        retryAfterMs: err.context?.retryAfterMs as number | undefined,
        cause: err,
      });
    default:
      throw new TtsSynthesisFailedError({ cause: err });
  }
}

API Reference

  • ELEVENLABS_CODES — frozen record { "<reason>": "THIRD_PARTY_ELEVENLABS.<reason>" }.
  • ElevenLabsErrorCode — TypeScript union of every code value.
  • ElevenLabsApiError — abstract base extending SuaveplanError.

Auth + request errors:

  • UnauthorizedError({ endpoint?, statusCode?, requestId?, ...base }) — AUTH.UNAUTHORIZED (HTTP 401).
  • ForbiddenError(...) — AUTH.FORBIDDEN (HTTP 403).
  • RequestInvalidError(...) — REQUEST.INVALID (other 4xx).
  • RequestValidationFailedError({ body?, ...request }) — REQUEST.VALIDATION_FAILED (HTTP 422); body preserved verbatim.
  • RequestNotFoundError(...) — REQUEST.NOT_FOUND (HTTP 404).

Rate limit:

  • RateLimitExceededError({ retryAfterMs?, ...request }) — RATE_LIMIT.EXCEEDED (HTTP 429).
  • RateLimitQuotaExhaustedError(...) — RATE_LIMIT.QUOTA_EXHAUSTED.

Server:

  • ServerTransientError(...) — SERVER.TRANSIENT (retryable 5xx).
  • ServerUnavailableError(...) — SERVER.UNAVAILABLE (non-retryable / circuit-open).

Network:

  • NetworkTimeoutError({ endpoint?, timeoutMs?, ...base }) — NETWORK.TIMEOUT.
  • NetworkAbortedError({ endpoint?, ...base }) — NETWORK.ABORTED.
  • NetworkUnreachableError({ endpoint?, ...base }) — NETWORK.UNREACHABLE.

Response / stream:

  • ResponseMalformedError({ endpoint?, bodyPreview?, ...base }) — RESPONSE.MALFORMED.
  • ResponseUnsupportedFormatError({ contentType?, ...base }) — RESPONSE.UNSUPPORTED_FORMAT.
  • StreamInterruptedError(...) — STREAM.INTERRUPTED.

WebSocket:

  • WebSocketConnectionFailedError({ code?, ...base }) — WEBSOCKET.CONNECTION_FAILED.
  • WebSocketProtocolError({ code?, ...base }) — WEBSOCKET.PROTOCOL.

Every constructor accepts { message?, cause?, context?, traceId? }.

Implementation Notes

  • The 17 codes partition [400, 600) HTTP statuses plus three networking modes plus three response / stream modes plus two WebSocket modes. The client never throws a generic ElevenLabsApiError — every thrown error is one of the typed subclasses.
  • RequestValidationFailedError.body should be the parsed JSON body (truncated to a reasonable bound by the client — currently 4 KB; see client.ts). Never put raw network buffers here.
  • bodyPreview on ResponseMalformedError is a short string (≤ 256 bytes) suitable for logs. Adapters should not re-serialise the full body into context.
  • All subclasses default to a stable English message. Localisation flows through MessageResolver on @suaveplan/error.