Skip to content

Error Handling

The KINGSTONE SDK throws KingstoneApiError for all API error responses. This class provides structured information about what went wrong, making it easy to handle different error types programmatically.

The KingstoneApiError Class

typescript
class KingstoneApiError extends Error {
  statusCode: number;     // HTTP status code (401, 404, 429, etc.)
  errorCode?: string;     // Machine-parseable code (e.g., "KS-4001")
  message: string;        // Human-readable error description
  retryable: boolean;     // true for 429 and 5xx errors
}

Every API error is mapped to an instance of KingstoneApiError. The errorCode uses the format KS-XXXX where the number indicates the category:

RangeCategoryExamples
KS-40xxAuthenticationMissing key, invalid key
KS-41xxValidationBad request body, wager out of range
KS-42xxGame/ResourceGame not found, queue not ready
KS-43xxSettlementAlready acknowledged, dispute conflict
KS-429xRate LimitSpin rate limit, API rate limit
KS-50xxServerInternal error, queue service error

Basic Error Handling

typescript
import { KingstoneClient, KingstoneApiError } from '@kingstone/sdk';

const client = new KingstoneClient({
  apiKey: 'ks_sandbox_your_key_here',
  sandbox: true,
});

try {
  const result = await client.spin({
    gameId: 1,
    playerId: 'player-abc',
    wagerUsd: 0.25,
  });
  console.log(`Payout: $${result.outcome.payoutUsd}`);
} catch (error) {
  if (error instanceof KingstoneApiError) {
    console.error(`[${error.errorCode}] ${error.message} (HTTP ${error.statusCode})`);
  } else {
    console.error('Network or unexpected error:', error);
  }
}

Handling Specific Error Codes

Use the errorCode to respond differently to different errors:

typescript
try {
  await client.spin({ gameId: 1, playerId: 'p1', wagerUsd: 0.25 });
} catch (error) {
  if (!(error instanceof KingstoneApiError)) throw error;

  switch (error.errorCode) {
    case 'KS-4001':
      // Missing API key — configuration problem
      console.error('API key not configured');
      break;

    case 'KS-4002':
      // Invalid or revoked API key
      console.error('API key is invalid — contact Predigy for a new key');
      break;

    case 'KS-4103':
      // Wager out of range
      console.error('Wager rejected — check min/max/increment limits');
      break;

    case 'KS-4201':
      // Game not found
      console.error('Game does not exist — check the gameId');
      break;

    case 'KS-4291':
      // Spin rate limit
      console.error('Too many spins — slow down and retry');
      break;

    default:
      console.error(`Unhandled: ${error.errorCode} — ${error.message}`);
  }
}

Retry Logic for Transient Errors

The retryable property is true for errors that are likely temporary:

  • 429 — Rate limit exceeded. Wait and try again.
  • 5xx — Server error. The issue is on KINGSTONE's side and may resolve on its own.

All other errors (authentication, validation, resource not found) are not retryable — sending the same request again will produce the same error.

Exponential Backoff Example

typescript
import { KingstoneApiError } from '@kingstone/sdk';

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelayMs = 1000,
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const isRetryable = error instanceof KingstoneApiError && error.retryable;
      const hasRetriesLeft = attempt < maxRetries;

      if (isRetryable && hasRetriesLeft) {
        const delay = baseDelayMs * Math.pow(2, attempt);
        console.log(`Retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      // Not retryable, or out of retries
      throw error;
    }
  }
  throw new Error('Unreachable');
}

// Usage
const result = await withRetry(
  () => client.spin({ gameId: 1, playerId: 'p1', wagerUsd: 0.25 }),
  3,    // max retries
  1000, // start with 1 second delay
);

Handling by HTTP Status

If you prefer to handle errors by HTTP status instead of error codes:

typescript
try {
  await client.getPlayer('nonexistent-id');
} catch (error) {
  if (!(error instanceof KingstoneApiError)) throw error;

  if (error.statusCode === 401) {
    // Authentication failed
    console.error('Check your API key');
  } else if (error.statusCode === 404) {
    // Resource not found
    console.error('Player has never spun');
  } else if (error.statusCode === 429) {
    // Rate limited
    console.error('Slow down — check X-RateLimit-* headers');
  } else if (error.statusCode >= 500) {
    // Server error — retry
    console.error('Server error — retry with backoff');
  } else {
    // Other client error (400, 409, etc.)
    console.error(`Client error: ${error.message}`);
  }
}

Timeout Errors

If a request exceeds the configured timeoutMs (default: 30 seconds), the SDK throws a KingstoneApiError with status code 408:

typescript
try {
  await client.spin({ gameId: 1, playerId: 'p1', wagerUsd: 0.25 });
} catch (error) {
  if (error instanceof KingstoneApiError && error.statusCode === 408) {
    console.error('Request timed out — the server may be overloaded');
  }
}

You can adjust the timeout when creating the client:

typescript
const client = new KingstoneClient({
  apiKey: 'ks_sandbox_your_key_here',
  sandbox: true,
  timeoutMs: 10000, // 10 seconds
});

Network Errors

If the request fails at the network level (DNS resolution, connection refused, etc.), the SDK throws the native fetch error, not KingstoneApiError. Always check instanceof before accessing error-specific properties:

typescript
try {
  await client.listGames();
} catch (error) {
  if (error instanceof KingstoneApiError) {
    // API returned an error response
    console.error(`API error: ${error.errorCode}`);
  } else if (error instanceof TypeError) {
    // Network-level failure (fetch throws TypeError)
    console.error('Network error — check connectivity and base URL');
  } else {
    console.error('Unexpected error:', error);
  }
}

Next Steps

KINGSTONE by Predigy Inc.