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
contextis 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 extendingSuaveplanError.
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 genericElevenLabsApiError— every thrown error is one of the typed subclasses. RequestValidationFailedError.bodyshould be the parsed JSON body (truncated to a reasonable bound by the client — currently 4 KB; seeclient.ts). Never put raw network buffers here.bodyPreviewonResponseMalformedErroris 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
MessageResolveron@suaveplan/error.