Skip to main content

Error Handling

The Ashr Labs SDK provides specific exception classes for different error scenarios, making it easy to handle errors gracefully.

Exception Hierarchy

AshrLabsError (base)
├── AuthenticationError (401)
├── AuthorizationError (403)
├── NotFoundError (404)
├── ValidationError (422)
├── RateLimitError (429)
└── ServerError (5xx)

Exception Classes

AshrLabsError

Base exception for all SDK errors.

Properties:

  • message (string): Error message
  • statusCode (number | null): HTTP status code if applicable
  • response (Record<string, unknown> | null): Raw response body if available
import { AshrLabsError } from "ashr-labs";

try {
const result = await client.getDataset(42);
} catch (e) {
if (e instanceof AshrLabsError) {
console.log(`Error: ${e.message}`);
console.log(`Status Code: ${e.statusCode}`);
console.log(`Response: ${JSON.stringify(e.response)}`);
}
}

AuthenticationError

Raised when API key authentication fails (HTTP 401).

Common causes:

  • Invalid API key
  • Expired API key
  • Revoked API key
  • Malformed Authorization header
import { AuthenticationError } from "ashr-labs";

try {
const datasets = await client.listDatasets();
} catch (e) {
if (e instanceof AuthenticationError) {
console.log("Authentication failed!");
console.log("Please check your API key is valid and not expired.");
// Log the user out or prompt for new credentials
}
}

AuthorizationError

Raised when the API key lacks permission (HTTP 403).

Common causes:

  • Trying to access a resource in a different tenant
  • Using API key for OAuth-only endpoints
  • Insufficient scopes on the API key
import { AuthorizationError } from "ashr-labs";

try {
// Attempting to access another tenant's data
const dataset = await client.getDataset(999);
} catch (e) {
if (e instanceof AuthorizationError) {
console.log("Access denied!");
console.log("You don't have permission to access this resource.");
}
}

NotFoundError

Raised when a requested resource doesn't exist (HTTP 404).

Common causes:

  • Invalid resource ID
  • Resource was deleted
  • Typo in the ID
import { NotFoundError } from "ashr-labs";

try {
const dataset = await client.getDataset(99999);
} catch (e) {
if (e instanceof NotFoundError) {
console.log(`Dataset not found: ${e.message}`);
// Handle missing resource gracefully
}
}

ValidationError

Raised when request validation fails (HTTP 422).

Common causes:

  • Missing required fields
  • Invalid field types
  • Schema validation failure
  • Invalid parameter values
import { ValidationError } from "ashr-labs";

try {
const run = await client.createRun(-1, {}); // Invalid ID
} catch (e) {
if (e instanceof ValidationError) {
console.log(`Validation failed: ${e.message}`);
// Show user which fields need correction
}
}

RateLimitError

Raised when rate limits are exceeded (HTTP 429).

Common causes:

  • Too many requests in a short period
  • Burst limit exceeded
import { RateLimitError } from "ashr-labs";

async function fetchWithRetry(
client: AshrLabsClient,
datasetId: number,
maxRetries = 3,
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.getDataset(datasetId);
} catch (e) {
if (e instanceof RateLimitError && attempt < maxRetries - 1) {
const waitTime = 2 ** attempt * 1000; // Exponential backoff
console.log(`Rate limited. Waiting ${waitTime}ms...`);
await new Promise((r) => setTimeout(r, waitTime));
} else {
throw e;
}
}
}
}

ServerError

Raised when the server encounters an internal error (HTTP 5xx).

Common causes:

  • Server maintenance
  • Temporary outage
  • Internal service failure
import { ServerError } from "ashr-labs";

try {
const datasets = await client.listDatasets();
} catch (e) {
if (e instanceof ServerError) {
console.log("Server error occurred. Please try again later.");
// Log the error for monitoring
console.error(`Server error: ${e.statusCode} - ${e.message}`);
}
}

Best Practices

1. Catch Specific Exceptions

Always catch the most specific exception first:

import {
AshrLabsClient,
AuthenticationError,
AuthorizationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
AshrLabsError,
} from "ashr-labs";

try {
const dataset = await client.getDataset(42);
} catch (e) {
if (e instanceof AuthenticationError) {
// Handle auth failure - maybe refresh credentials
handleAuthFailure();
} else if (e instanceof AuthorizationError) {
// Handle permission denied
showPermissionError();
} else if (e instanceof NotFoundError) {
// Handle missing resource
showNotFoundMessage();
} else if (e instanceof ValidationError) {
// Handle validation errors
showValidationErrors(e.response);
} else if (e instanceof RateLimitError) {
// Handle rate limiting
scheduleRetry();
} else if (e instanceof ServerError) {
// Handle server errors
showTemporaryError();
} else if (e instanceof AshrLabsError) {
// Catch-all for other API errors
logUnexpectedError(e);
} else {
throw e;
}
}

2. Implement Retry Logic

For transient errors, implement retry with exponential backoff:

import { RateLimitError, ServerError, AshrLabsError } from "ashr-labs";

async function robustRequest<T>(
fn: () => Promise<T>,
maxRetries = 3,
): Promise<T> {
let lastError: Error | null = null;

for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (e) {
if (e instanceof RateLimitError || e instanceof ServerError) {
lastError = e;
if (attempt < maxRetries - 1) {
const waitTime = 2 ** attempt * 1000 + Math.random() * 100;
await new Promise((r) => setTimeout(r, waitTime));
continue;
}
}
// Don't retry auth/validation/not-found errors
throw e;
}
}

throw lastError;
}

// Usage
const dataset = await robustRequest(() => client.getDataset(42));

3. Log Errors Appropriately

try {
const result = await client.createRun(42, runData);
} catch (e) {
if (e instanceof AuthenticationError) {
console.error(`Auth failed: ${e.message}`);
throw e;
} else if (e instanceof ValidationError) {
console.warn(`Validation error: ${e.message}`, { response: e.response });
throw e;
} else if (e instanceof ServerError) {
console.error(`Server error ${e.statusCode}: ${e.message}`);
throw e;
}
}

4. User-Friendly Error Messages

Map technical errors to user-friendly messages:

import {
AuthenticationError,
AuthorizationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
AshrLabsError,
} from "ashr-labs";

function getUserMessage(error: unknown): string {
if (error instanceof AuthenticationError)
return "Your session has expired. Please log in again.";
if (error instanceof AuthorizationError)
return "You don't have permission to perform this action.";
if (error instanceof NotFoundError)
return "The requested item could not be found.";
if (error instanceof ValidationError)
return "Please check your input and try again.";
if (error instanceof RateLimitError)
return "Too many requests. Please wait a moment and try again.";
if (error instanceof ServerError)
return "We're experiencing technical difficulties. Please try again later.";
return "An unexpected error occurred.";
}

5. Wrapper Functions for Cleanup

async function safeApiOperation<T>(
operationName: string,
fn: () => Promise<T>,
): Promise<T> {
try {
return await fn();
} catch (e) {
if (e instanceof AshrLabsError) {
console.error(`${operationName} failed: ${e}`);
// Perform any necessary cleanup
}
throw e;
}
}

// Usage
const run = await safeApiOperation("Create Run", () =>
client.createRun(42, runData),
);

Network Errors

The SDK also handles network-level errors:

import { AshrLabsError } from "ashr-labs";

try {
const result = await client.healthCheck();
} catch (e) {
if (e instanceof AshrLabsError) {
if (e.message.includes("Network error")) {
console.log("Cannot connect to the API. Check your internet connection.");
} else if (e.message.toLowerCase().includes("timed out")) {
console.log("Request timed out. The server may be slow or unreachable.");
} else {
throw e;
}
}
}

Debugging Tips

Check the raw response in exceptions:

try {
const result = await client.getDataset(42);
} catch (e) {
if (e instanceof AshrLabsError) {
console.log(`Status: ${e.statusCode}`);
console.log(`Message: ${e.message}`);
console.log(`Raw response: ${JSON.stringify(e.response)}`);
}
}

The toString() method on errors provides a formatted summary:

try {
await client.getDataset(99999);
} catch (e) {
if (e instanceof AshrLabsError) {
console.log(e.toString());
// => "[404] Dataset not found"
}
}