Skip to content
Get started
SDKs
TypeScript

Error Handling

Build robust TypeScript applications with the Tabstack SDK's comprehensive error class hierarchy and handling patterns.

Building robust applications requires proper error handling. The Tabstack SDK provides a comprehensive error class hierarchy and patterns for handling different error scenarios.

The SDK includes specific error classes for different failure scenarios:

TabstackError (base class)
├── APIError (with status code)
│ ├── AuthenticationError (401)
│ ├── PermissionDeniedError (403)
│ ├── NotFoundError (404)
│ ├── ConflictError (409)
│ ├── UnprocessableEntityError (422)
│ ├── RateLimitError (429)
│ ├── InternalServerError (500+)
│ └── BadRequestError (400)
├── APIConnectionError
└── APIConnectionTimeoutError
import Tabstack, {
TabstackError,
APIError,
AuthenticationError,
PermissionDeniedError,
NotFoundError,
ConflictError,
UnprocessableEntityError,
RateLimitError,
InternalServerError,
BadRequestError,
APIConnectionError,
APIConnectionTimeoutError
} from '@tabstack/sdk';

Base error class for all SDK errors. All other error classes extend this.

Properties:

  • message: string - Error message
  • name: string - Error class name
  • cause?: Error - Optional underlying error

Generic API error with HTTP status code. Used for errors that don’t match specific error classes.

Properties:

  • status: number - HTTP status code
  • message: string - Error message

Thrown when authentication fails.

Common causes:

  • Invalid API key
  • Missing API key
  • Expired API key

Thrown when you don’t have permission to access a resource.

Common causes:

  • Insufficient permissions
  • Resource access denied

Thrown when a requested resource is not found.

Common causes:

  • Invalid endpoint
  • Resource doesn’t exist

Thrown when there’s a conflict with the current state of a resource.

Common causes:

  • Duplicate resource creation
  • Version conflicts

Thrown when the provided data is invalid or inaccessible.

Common causes:

  • Malformed URL format
  • URL points to private/internal resources (localhost, 127.0.0.1, private IPs)
  • URL is unreachable
  • Invalid JSON schema format

Thrown when you’ve exceeded the rate limit.

Common causes:

  • Too many requests in a short period
  • Quota exceeded

Thrown when the server encounters an internal error.

Common causes:

  • Failed to fetch the URL
  • Page content too large
  • Failed to generate/extract data
  • Server-side processing error

Thrown when the request is malformed or invalid.

Common causes:

  • Missing required parameters
  • Invalid JSON schema format
  • Malformed request body

Thrown when the SDK cannot connect to the API.

Common causes:

  • Network connectivity issues
  • DNS resolution failures
  • Firewall blocking requests

Thrown when the connection times out.

Common causes:

  • Slow network connection
  • Server not responding
  • Request took too long
import Tabstack, { TabstackError } from '@tabstack/sdk';
const client = new Tabstack({
apiKey: process.env.TABSTACK_API_KEY!
});
try {
const result = await client.extract.markdown({ url: 'https://example.com' });
console.log(result.content);
} catch (error) {
if (error instanceof TabstackError) {
console.error('API error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
import Tabstack, {
AuthenticationError,
UnprocessableEntityError,
InternalServerError,
RateLimitError,
TabstackError
} from '@tabstack/sdk';
const client = new Tabstack({
apiKey: process.env.TABSTACK_API_KEY!
});
async function extractWithErrorHandling(url: string) {
try {
const result = await client.extract.markdown({ url });
return result;
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed. Please check your API key.');
} else if (error instanceof UnprocessableEntityError) {
console.error(`Invalid or inaccessible URL: ${url}`);
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded. Waiting before retry...');
await new Promise(resolve => setTimeout(resolve, 60000));
return extractWithErrorHandling(url);
} else if (error instanceof InternalServerError) {
console.error('Server error occurred. Retrying in 5 seconds...');
await new Promise(resolve => setTimeout(resolve, 5000));
return extractWithErrorHandling(url);
} else if (error instanceof TabstackError) {
console.error('API error:', error.message);
} else {
console.error('Unexpected error:', error);
throw error;
}
}
}
import Tabstack, { InternalServerError, RateLimitError } from '@tabstack/sdk';
async function extractWithRetry(
client: Tabstack,
url: string,
maxRetries: number = 3,
baseDelay: number = 1000
) {
let lastError: Error;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await client.extract.markdown({ url });
return result;
} catch (error) {
lastError = error;
if (error instanceof InternalServerError || error instanceof RateLimitError) {
if (attempt < maxRetries - 1) {
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
throw error;
}
}
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Usage
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
try {
const result = await extractWithRetry(client, 'https://example.com');
console.log('Success:', result.content);
} catch (error) {
console.error('Failed:', error.message);
}
import Tabstack, { TabstackError } from '@tabstack/sdk';
interface BatchResult<T> {
url: string;
success: boolean;
data?: T;
error?: string;
}
async function batchExtractWithErrorHandling<T>(
client: Tabstack,
urls: string[],
schema: object
): Promise<BatchResult<T>[]> {
const results: BatchResult<T>[] = [];
for (const url of urls) {
try {
const result = await client.extract.json({ url, json_schema: schema });
results.push({
url,
success: true,
data: result as T
});
console.log(`Success: ${url}`);
} catch (error) {
const errorMessage = error instanceof TabstackError
? error.message
: 'Unknown error';
results.push({
url,
success: false,
error: errorMessage
});
console.error(`Failed: ${url} - ${errorMessage}`);
}
// Respectful delay between requests
await new Promise(resolve => setTimeout(resolve, 500));
}
return results;
}
// Usage
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
const urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
];
const schema = {
type: 'object',
properties: {
title: { type: 'string' }
}
};
const results = await batchExtractWithErrorHandling(client, urls, schema);
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log(`\nBatch Complete: ${successful.length} succeeded, ${failed.length} failed`);
if (failed.length > 0) {
console.log('\nFailed URLs:');
failed.forEach(r => console.log(` - ${r.url}: ${r.error}`));
}
import Tabstack, { TabstackError, APIError } from '@tabstack/sdk';
interface ErrorLog {
timestamp: Date;
operation: string;
url?: string;
errorType: string;
message: string;
status?: number;
stack?: string;
}
class ErrorTracker {
private errors: ErrorLog[] = [];
log(operation: string, error: Error, url?: string) {
const errorLog: ErrorLog = {
timestamp: new Date(),
operation,
url,
errorType: error.constructor.name,
message: error.message,
stack: error.stack
};
if (error instanceof APIError) {
errorLog.status = error.status;
}
this.errors.push(errorLog);
console.error(`[${errorLog.timestamp.toISOString()}] ${operation} failed:`, error.message);
}
getErrors() {
return this.errors;
}
getSummary() {
const byType = this.errors.reduce((acc, error) => {
acc[error.errorType] = (acc[error.errorType] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return {
total: this.errors.length,
byType
};
}
exportToJSON() {
return JSON.stringify(this.errors, null, 2);
}
}
// Usage
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
const errorTracker = new ErrorTracker();
async function monitoredExtract(url: string) {
try {
return await client.extract.markdown({ url });
} catch (error) {
errorTracker.log('extract.markdown', error, url);
throw error;
}
}
const urls = ['https://example.com/page1', 'https://example.com/page2'];
for (const url of urls) {
try {
await monitoredExtract(url);
} catch (error) {
// Error already logged, continue processing
}
}
console.log('\nError Summary:', errorTracker.getSummary());

Automate operations stream events and require different error handling:

import Tabstack, { TabstackError } from '@tabstack/sdk';
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
async function automateWithErrorHandling(task: string, url: string) {
try {
const stream = await client.agent.automate({
task,
url,
guardrails: 'browse and extract only'
});
for await (const event of stream) {
if (event.event === 'error') {
const error = event.data?.error;
console.error('Automation error:', error);
throw new Error(`Automation failed: ${error}`);
}
if (event.event === 'task:aborted') {
const reason = event.data?.reason;
console.warn('Task aborted:', reason);
throw new Error(`Task aborted: ${reason}`);
}
if (event.event === 'task:validation_error') {
const error = event.data?.error;
console.error('Validation failed:', error);
}
if (event.event === 'task:completed') {
const result = event.data?.finalAnswer;
console.log('Success:', result);
return result;
}
}
} catch (error) {
if (error instanceof TabstackError) {
console.error('API error during automation:', error.message);
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}
// Usage
try {
await automateWithErrorHandling(
'Extract the top 5 articles',
'https://news.example.com'
);
} catch (error) {
console.error('Automation failed completely:', error.message);
}
// Bad: No error handling
const result = await client.extract.markdown({ url });
// Good: Proper error handling
try {
const result = await client.extract.markdown({ url });
} catch (error) {
// Handle error
}
try {
// API call
} catch (error) {
if (error instanceof AuthenticationError) {
// Handle auth error
} else if (error instanceof UnprocessableEntityError) {
// Handle URL error
} else if (error instanceof TabstackError) {
// Handle other API errors
} else {
// Handle unexpected errors
}
}

3. Implement Retry Logic for Transient Errors

Section titled “3. Implement Retry Logic for Transient Errors”
if (error instanceof InternalServerError || error instanceof RateLimitError) {
// Implement retry with backoff
} else if (error instanceof AuthenticationError || error instanceof BadRequestError) {
// Don't retry - fix the issue first
throw error;
}
try {
const result = await client.extract.json({ url, json_schema: schema });
} catch (error) {
console.error(`Failed to extract from ${url}:`, {
errorType: error.constructor.name,
message: error.message,
url,
timestamp: new Date().toISOString()
});
}
try {
const result = await client.extract.markdown({ url });
} catch (error) {
if (error instanceof AuthenticationError) {
return 'Your API key is invalid. Please check your credentials and try again.';
} else if (error instanceof UnprocessableEntityError) {
return 'The URL you provided cannot be accessed. Please verify it is correct and publicly accessible.';
} else {
return 'An unexpected error occurred. Please try again later or contact support.';
}
}
let logFile;
try {
logFile = openLog();
const result = await client.extract.markdown({ url });
writeLog(logFile, result);
} catch (error) {
console.error('Error:', error);
throw error;
} finally {
if (logFile) {
closeLog(logFile);
}
}
try {
const result = await client.extract.markdown({ url });
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Please set a valid API key in TABSTACK_API_KEY environment variable');
process.exit(1);
}
}
try {
const result = await client.extract.markdown({ url: 'https://localhost:3000' });
} catch (error) {
if (error instanceof UnprocessableEntityError) {
console.error('Cannot access private URLs like localhost');
}
}
try {
const result = await client.extract.json({ url, json_schema: incompatibleSchema });
} catch (error) {
if (error instanceof InternalServerError && error.message.includes('failed to generate')) {
console.error('Schema does not match page content. Try generating a schema first.');
}
}