GeminiGemini
Demo environmentGet API key
  • Overview
  • Crypto Trading
  • Prediction Markets
  • Perpetuals
  • Stocks
  • API Reference
  • SDKs & Tools
Changelog
Gemini logoGemini logo

© 2026 Gemini Space Station, Inc.

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

Code
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:

Code
import { SdkError, ApiError, RateLimitError, AcceptTermsRequired, } from "@gemini-markets/sdk/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:

Code
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:

Code
const client = await createClient({ env: "sandbox", 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:

Code
import { serializeError } from "@gemini-markets/sdk/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:

Code
const client = await createClient({ env: "sandbox", auth, onDiagnostic: (event) => { // event.level: "debug" | "info" | "warn" | "error" // event.component: "rest" | "oauth" | "websocket" | "order_book" // event.name: specific event name // event.correlationId: stable ID for this request or WebSocket subscription // 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:

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

OpenTelemetry (optional)

The SDK includes an optional adapter at @gemini-markets/sdk/opentelemetry. It maps safe diagnostics to OpenTelemetry client spans for REST and OAuth requests, WebSocket requests and subscriptions, reconnects, and order-book recovery events. The adapter does not configure a provider or exporter, and the core SDK remains dependency-free unless this subpath is imported.

Install the OpenTelemetry API alongside the provider and exporter selected by your application:

TerminalCode
npm install @opentelemetry/api

Pass the tracer from your configured provider to the adapter and connect its diagnostic listener to the client:

Code
import { trace } from "@opentelemetry/api"; import { createOpenTelemetryHooks } from "@gemini-markets/sdk/opentelemetry"; import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const telemetry = createOpenTelemetryHooks({ tracer: trace.getTracer("my-trading-service"), }); const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey, apiSecret }), onDiagnostic: telemetry.onDiagnostic, }); // End active subscription spans during application shutdown. telemetry.shutdown();

Spans use SpanKind.CLIENT, standard HTTP method and response-status attributes where applicable, and Gemini-specific attributes for operation names, correlation IDs, retry counts, exchange request IDs, stream names, and symbols. Request and response bodies, credentials, tokens, signatures, and raw error bodies are not copied into spans. Configure and flush the provider through your application’s normal OpenTelemetry shutdown path.

By default, HTTP span names use the method and stable generated operation name when available (for example, POST trading.createNewOrder); otherwise they use only the method. Dynamic endpoint values are not used as span names. This adapter provides SDK-level logical spans; use your OpenTelemetry HTTP or undici instrumentation for wire-level HTTP context propagation, and avoid enabling two instrumentations for the same network span unless both layers are intentional.

Console logger

For development, use the built-in console logger:

Code
import { ConsoleLogger } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", 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 — timeouts, cancellation, pagination, and heartbeat
  • API Reference — all operations with their retry and validation behavior
  • WebSocket Sessions — reconnection and connection error recovery
  • Transport & Signing — retry policy internals
On this page
  • Error class hierarchy
  • Catching errors
  • Error metadata
  • Retries
  • Safe error serialization
  • Diagnostics
    • Diagnostic listener
    • OpenTelemetry (optional)
    • Console logger
    • What's redacted
  • What's next
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript