# TypeScript SDK — Error Handling

The SDK uses typed error classes so you can catch specific failure modes. Every SDK error extends `SdkError`.

## Error class hierarchy

```
SdkError                          Base class for all SDK errors
├── ApiError                      Any non-2xx REST response
│   ├── InvalidRequest            400 — malformed or rejected request
│   ├── InvalidNonce              400 — nonce reused or not increasing
│   ├── MissingNonce              400 — nonce not present in payload
│   ├── InvalidSignature          400 — HMAC signature mismatch
│   ├── MissingRole               403 — API key lacks required role
│   ├── AcceptTermsRequired       403 — must accept terms before trading
│   ├── NotFoundError             404 — resource does not exist
│   ├── InsufficientFunds         406 — not enough balance
│   ├── RateLimitError            429 — rate limit exceeded
│   └── ServiceUnavailable        5xx — exchange down or errored
├── ValidationError               Request body fails documented shape check
├── ConnectionError               WebSocket open failed or dropped
├── WebSocketRequestError         Non-success WebSocket method response
├── RequestTimeoutError           Operation exceeded deadline
├── RequestAbortedError           Caller cancelled via AbortSignal
├── OAuthStateError               OAuth callback state mismatch
├── OAuthAuthorizationError       OAuth authorization denied
├── OAuthTokenError               OAuth token endpoint failure
├── EndpointMismatch              Internal: payload path mismatch
└── ResyncRequiredError           Order book gap detected
```

## Catching errors

Catch broadly or narrowly depending on your needs:

```ts
import {
  SdkError,
  ApiError,
  RateLimitError,
  AcceptTermsRequired,
} from "gemini-markets/server";

try {
  await client.predictions.placeOrder({ /* ... */ });
} catch (err) {
  if (err instanceof AcceptTermsRequired) {
    // User must accept terms first
    await client.predictions.acceptTerms();
    // retry...
  } else if (err instanceof RateLimitError) {
    // Back off and retry
    console.log("Rate limited, status:", err.status);
  } else if (err instanceof ApiError) {
    // Any other API error
    console.log(err.status, err.reason, err.code, err.category);
  } else if (err instanceof SdkError) {
    // SDK-level error (timeout, connection, validation)
    console.log(err.message);
  }
}
```

## Error metadata

Every `ApiError` carries structured fields for programmatic handling:

```ts
catch (err) {
  if (err instanceof ApiError) {
    err.status;     // HTTP status code (400, 403, 429, etc.)
    err.reason;     // Server error string ("InvalidNonce", "RateLimit", etc.)
    err.code;       // Stable SDK code ("invalid_request", "rate_limited", etc.)
    err.category;   // Error family ("validation", "authentication", "rate_limit", etc.)
    err.serverCode; // Raw server error code, if present
    err.metadata;   // Request metadata (endpoint, method, correlation ID, status)
  }
}
```

The `code` and `category` fields are stable across SDK versions — use them for programmatic branching. The `reason` field is the verbatim server string and may change.

## Retries

The SDK retries automatically for **generated safe-read operations only** (GET-equivalent endpoints). Retries trigger on:
- Network failures
- HTTP 429 (rate limit) — respects `Retry-After` header
- HTTP 502, 503, 504 (transient server errors)

**Mutating operations are never retried.** A failed order placement stays failed — you decide whether to retry.

Configure retry behavior:

```ts
const client = await createClient({
  auth,
  maxRetries: 3,       // default: 5
  backoff: {
    baseMs: 500,       // default: 500
    capMs: 15_000,     // default: 30_000
    factor: 2,         // default: 2
  },
});
```

## Safe error serialization

Use `serializeError()` to log errors safely. It strips raw response bodies and credentials while preserving structure:

```ts
import { serializeError } from "gemini-markets/server";

try {
  await client.trading.createNewOrder({ /* ... */ });
} catch (err) {
  // Safe for logging — no secrets, no raw bodies
  console.log(JSON.stringify(serializeError(err)));

  // Include raw body only for debugging (treat as sensitive)
  console.log(serializeError(err, { includeRawBody: true }));
}
```

The serialized output includes: `name`, `message` (redacted), `status`, `reason` (only recognized values), `code`, `category`, `metadata` (endpoint, method, correlation ID), and `operationContext`.

## Diagnostics

The SDK emits structured diagnostic events across REST, OAuth, WebSocket, and order-book operations. Diagnostics are silent by default.

### Diagnostic listener

Receive every event as a structured object:

```ts
const client = await createClient({
  auth,
  onDiagnostic: (event) => {
    // event.level: "debug" | "info" | "warn" | "error"
    // event.component: "rest" | "oauth" | "websocket" | "order_book"
    // event.name: specific event name
    // event.response: safe metadata (endpoint, status, correlation ID)
    myTelemetry.record(event);
  },
});
```

When using `OAuthAuth`, pass the same `onDiagnostic` callback to include token exchange and refresh events:

```ts
const auth = new OAuthAuth({
  // ...
  onDiagnostic: (event) => myTelemetry.record(event),
});
```

### Console logger

For development, use the built-in console logger:

```ts
import { ConsoleLogger } from "gemini-markets/server";

const client = await createClient({
  auth,
  logger: new ConsoleLogger({ minLevel: "debug" }),
});
```

Log levels: `debug`, `info`, `warn`, `error`. Set `minLevel` to control verbosity.

### What's redacted

Diagnostic events and serialized errors **never include**: request bodies, response bodies, credentials, signatures, tokens, API keys, or nonces. They **do include**: endpoint paths, HTTP methods, status codes, correlation IDs, exchange request IDs, rate-limit headers, retry counts, and content types.

Use `serializeError(err, { includeRawBody: true })` only when you need the raw body for debugging, and treat that output as sensitive.

## What's next

- [Patterns & recipes](/tools/typescript-sdk/patterns) — timeouts, cancellation, pagination, and heartbeat
- [API Reference](/tools/typescript-sdk/reference/overview) — all operations with their retry and validation behavior
- [WebSocket Sessions](/tools/typescript-sdk/deep-dives/websocket-sessions) — reconnection and connection error recovery
- [Transport & Signing](/tools/typescript-sdk/deep-dives/transport-and-signing) — retry policy internals
