Errors

Every failure throws a typed error chosen by HTTP status, carrying the API's error code and request id.

Every API error throws. The class is chosen by HTTP status; the machine-readable code rides along for finer dispatch.

import { NotFoundError, RateLimitError } from '@ellipsis-dev/sdk';

try {
  const { session } = await client.sessions.get('session_missing');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log(error.code, error.requestId);
  } else if (error instanceof RateLimitError) {
    // retries were already exhausted
  } else {
    throw error;
  }
}

The hierarchy

Error
└── EllipsisError
    ├── TransportError        the request never got a response (DNS, TLS, timeout)
    └── APIError              a non-2xx response
        ├── AuthenticationError   401  token missing, invalid, or revoked
        ├── ForbiddenError        403  authenticated, not allowed
        ├── NotFoundError         404  no such resource in this account
        ├── ConflictError         409  conflicts with current state
        ├── UnprocessableError    422  request shape failed validation
        ├── RateLimitError        429  slow down
        └── ServerError           5xx  an error on our side

Check instanceof EllipsisError to catch everything this SDK throws, or instanceof APIError for anything the API answered. Read .status when you care about a code the hierarchy does not name.

What an APIError carries

PropertyValue
statusThe HTTP status.
codeThe API's stable error code, or null when the response carried no envelope.
messageThe human-readable message, also the Error message.
requestIdThe id to quote in a support request, or null.
bodyThe parsed response body, typed unknown.

Switch on code, never on message text. The codes are an open vocabulary: new ones appear without a version break, so treat an unrecognized code by its status.

import { APIError } from '@ellipsis-dev/sdk';

try {
  await handle.send('keep going');
} catch (error) {
  if (error instanceof APIError && error.code === 'session_finished') {
    handle = await client.sessions.run({ prompt: 'keep going' });
  } else {
    throw error;
  }
}

Retries happen first

429, 502, 503, and 504 are retried automatically with backoff before any error reaches you, maxRetries times. A RateLimitError in your code therefore means the retries were already exhausted. Transport failures are retried the same way, so a TransportError means the request never landed after every attempt.

Errors are not typed on the method

TypeScript has no checked exceptions, so a method's signature says nothing about what it throws. The list above is the whole surface: anything else escaping the client is a bug worth reporting.