Error Handling
Building robust applications requires proper error handling. The TABStack SDK provides a comprehensive error class hierarchy and patterns for handling different error scenarios.
Error Class Hierarchy
The SDK includes specific error classes for different failure scenarios:
TABStackError (base class)
├── BadRequestError (400)
├── UnauthorizedError (401)
├── InvalidURLError (422)
├── ServerError (500)
├── ServiceUnavailableError (503)
└── APIError (generic, with status code)
Importing Error Classes
- TypeScript
- JavaScript
import {
TABStack,
TABStackError,
BadRequestError,
UnauthorizedError,
InvalidURLError,
ServerError,
ServiceUnavailableError,
APIError
} from '@tabstack/sdk';
const {
TABStack,
TABStackError,
BadRequestError,
UnauthorizedError,
InvalidURLError,
ServerError,
ServiceUnavailableError,
APIError
} = require('@tabstack/sdk');
Error Classes Reference
TABStackError
Base error class for all SDK errors. All other error classes extend this.
Properties:
message: string- Error messagename: string- Error class namecause?: Error- Optional underlying error
BadRequestError (400)
Thrown when the request is malformed or invalid.
Common causes:
- Missing required parameters
- Invalid JSON schema format
- Malformed request body
UnauthorizedError (401)
Thrown when authentication fails.
Common causes:
- Invalid API key
- Missing API key
- Expired API key
InvalidURLError (422)
Thrown when the provided URL 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
ServerError (500)
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
ServiceUnavailableError (503)
Thrown when the service is temporarily unavailable.
Common causes:
- Server maintenance
- Temporary overload
- Rate limiting
APIError
Generic API error with HTTP status code. Used for errors that don't match specific error classes.
Properties:
statusCode: number- HTTP status codemessage: string- Error message
Basic Error Handling
Simple Try-Catch
- TypeScript
- JavaScript
import { TABStack, TABStackError } from '@tabstack/sdk';
const tabs = new TABStack({
apiKey: process.env.TABSTACK_API_KEY!
});
try {
const result = await tabs.extract.markdown('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);
}
}
const { TABStack, TABStackError } = require('@tabstack/sdk');
const tabs = new TABStack({
apiKey: process.env.TABSTACK_API_KEY
});
try {
const result = await tabs.extract.markdown('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);
}
}
Handling Specific Error Types
- TypeScript
- JavaScript
import {
TABStack,
UnauthorizedError,
InvalidURLError,
ServerError,
TABStackError
} from '@tabstack/sdk';
const tabs = new TABStack({
apiKey: process.env.TABSTACK_API_KEY!
});
async function extractWithErrorHandling(url: string) {
try {
const result = await tabs.extract.markdown(url);
return result;
} catch (error) {
if (error instanceof UnauthorizedError) {
console.error('Authentication failed. Please check your API key.');
// Notify user to update API key
} else if (error instanceof InvalidURLError) {
console.error(`Invalid or inaccessible URL: ${url}`);
// Log and skip this URL
} else if (error instanceof ServerError) {
console.error('Server error occurred. Retrying in 5 seconds...');
// Implement retry logic
await new Promise(resolve => setTimeout(resolve, 5000));
return extractWithErrorHandling(url); // Retry
} else if (error instanceof TABStackError) {
console.error('API error:', error.message);
} else {
console.error('Unexpected error:', error);
throw error; // Re-throw unexpected errors
}
}
}
const {
TABStack,
UnauthorizedError,
InvalidURLError,
ServerError,
TABStackError
} = require('@tabstack/sdk');
const tabs = new TABStack({
apiKey: process.env.TABSTACK_API_KEY
});
async function extractWithErrorHandling(url) {
try {
const result = await tabs.extract.markdown(url);
return result;
} catch (error) {
if (error instanceof UnauthorizedError) {
console.error('Authentication failed. Please check your API key.');
// Notify user to update API key
} else if (error instanceof InvalidURLError) {
console.error(`Invalid or inaccessible URL: ${url}`);
// Log and skip this URL
} else if (error instanceof ServerError) {
console.error('Server error occurred. Retrying in 5 seconds...');
// Implement retry logic
await new Promise(resolve => setTimeout(resolve, 5000));
return extractWithErrorHandling(url); // Retry
} else if (error instanceof TABStackError) {
console.error('API error:', error.message);
} else {
console.error('Unexpected error:', error);
throw error; // Re-throw unexpected errors
}
}
}
Advanced Error Handling Patterns
Retry Logic with Exponential Backoff
- TypeScript
- JavaScript
import { TABStack, ServerError, ServiceUnavailableError } from '@tabstack/sdk';
async function extractWithRetry(
tabs: TABStack,
url: string,
maxRetries: number = 3,
baseDelay: number = 1000
) {
let lastError: Error;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await tabs.extract.markdown(url);
return result;
} catch (error) {
lastError = error;
// Only retry on server errors
if (error instanceof ServerError || error instanceof ServiceUnavailableError) {
if (attempt < maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s, 8s, etc.
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;
}
}
// Don't retry other errors
throw error;
}
}
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Usage
const tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY! });
try {
const result = await extractWithRetry(tabs, 'https://example.com');
console.log('Success:', result.content);
} catch (error) {
console.error('Failed:', error.message);
}
const { TABStack, ServerError, ServiceUnavailableError } = require('@tabstack/sdk');
async function extractWithRetry(
tabs,
url,
maxRetries = 3,
baseDelay = 1000
) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await tabs.extract.markdown(url);
return result;
} catch (error) {
lastError = error;
// Only retry on server errors
if (error instanceof ServerError || error instanceof ServiceUnavailableError) {
if (attempt < maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s, 8s, etc.
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;
}
}
// Don't retry other errors
throw error;
}
}
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Usage
const tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY });
try {
const result = await extractWithRetry(tabs, 'https://example.com');
console.log('Success:', result.content);
} catch (error) {
console.error('Failed:', error.message);
}
Batch Processing with Error Tracking
- TypeScript
- JavaScript
import { TABStack, TABStackError } from '@tabstack/sdk';
interface BatchResult<T> {
url: string;
success: boolean;
data?: T;
error?: string;
}
async function batchExtractWithErrorHandling<T>(
tabs: TABStack,
urls: string[],
schema: object
): Promise<BatchResult<T>[]> {
const results: BatchResult<T>[] = [];
for (const url of urls) {
try {
const result = await tabs.extract.json<T>(url, schema);
results.push({
url,
success: true,
data: result.data
});
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 tabs = 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(tabs, urls, schema);
// Summary
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}`));
}
const { TABStack, TABStackError } = require('@tabstack/sdk');
async function batchExtractWithErrorHandling(tabs, urls, schema) {
const results = [];
for (const url of urls) {
try {
const result = await tabs.extract.json(url, schema);
results.push({
url,
success: true,
data: result.data
});
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 tabs = 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(tabs, urls, schema);
// Summary
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}`));
}
Error Logging and Monitoring
- TypeScript
- JavaScript
import { TABStack, TABStackError, APIError } from '@tabstack/sdk';
interface ErrorLog {
timestamp: Date;
operation: string;
url?: string;
errorType: string;
message: string;
statusCode?: 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.statusCode = error.statusCode;
}
this.errors.push(errorLog);
// Also log to console
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 tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY! });
const errorTracker = new ErrorTracker();
async function monitoredExtract(url: string) {
try {
return await tabs.extract.markdown(url);
} catch (error) {
errorTracker.log('extract.markdown', error, url);
throw error;
}
}
// Run multiple operations
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
}
}
// Print summary
console.log('\nError Summary:', errorTracker.getSummary());
// Export logs if needed
// fs.writeFileSync('error-log.json', errorTracker.exportToJSON());
const { TABStack, APIError } = require('@tabstack/sdk');
class ErrorTracker {
constructor() {
this.errors = [];
}
log(operation, error, url) {
const errorLog = {
timestamp: new Date(),
operation,
url,
errorType: error.constructor.name,
message: error.message,
stack: error.stack
};
if (error instanceof APIError) {
errorLog.statusCode = error.statusCode;
}
this.errors.push(errorLog);
// Also log to console
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;
}, {});
return {
total: this.errors.length,
byType
};
}
exportToJSON() {
return JSON.stringify(this.errors, null, 2);
}
}
// Usage
const tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY });
const errorTracker = new ErrorTracker();
async function monitoredExtract(url) {
try {
return await tabs.extract.markdown(url);
} catch (error) {
errorTracker.log('extract.markdown', error, url);
throw error;
}
}
// Run multiple operations
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
}
}
// Print summary
console.log('\nError Summary:', errorTracker.getSummary());
// Export logs if needed
// fs.writeFileSync('error-log.json', errorTracker.exportToJSON());
Automate Error Handling
Automate operations stream events and require different error handling:
- TypeScript
- JavaScript
import { TABStack, TABStackError } from '@tabstack/sdk';
const tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY! });
async function automateWithErrorHandling(task: string, options: any) {
try {
for await (const event of tabs.automate.execute(task, options)) {
// Handle error events within the stream
if (event.type === 'error') {
const error = event.data.get('error');
console.error('Automation error:', error);
// Decide whether to continue or abort
throw new Error(`Automation failed: ${error}`);
}
if (event.type === 'task:aborted') {
const reason = event.data.get('reason');
console.warn('Task aborted:', reason);
throw new Error(`Task aborted: ${reason}`);
}
if (event.type === 'task:validation_error') {
const error = event.data.get('error');
console.error('Validation failed:', error);
// May retry or abort based on your needs
}
if (event.type === 'task:completed') {
const result = event.data.get('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',
{ url: 'https://news.example.com' }
);
} catch (error) {
console.error('Automation failed completely:', error.message);
}
const { TABStack, TABStackError } = require('@tabstack/sdk');
const tabs = new TABStack({ apiKey: process.env.TABSTACK_API_KEY });
async function automateWithErrorHandling(task, options) {
try {
for await (const event of tabs.automate.execute(task, options)) {
// Handle error events within the stream
if (event.type === 'error') {
const error = event.data.get('error');
console.error('Automation error:', error);
// Decide whether to continue or abort
throw new Error(`Automation failed: ${error}`);
}
if (event.type === 'task:aborted') {
const reason = event.data.get('reason');
console.warn('Task aborted:', reason);
throw new Error(`Task aborted: ${reason}`);
}
if (event.type === 'task:validation_error') {
const error = event.data.get('error');
console.error('Validation failed:', error);
// May retry or abort based on your needs
}
if (event.type === 'task:completed') {
const result = event.data.get('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',
{ url: 'https://news.example.com' }
);
} catch (error) {
console.error('Automation failed completely:', error.message);
}
Best Practices
1. Always Use Try-Catch
Never make API calls without error handling:
// ❌ Bad: No error handling
const result = await tabs.extract.markdown(url);
// ✅ Good: Proper error handling
try {
const result = await tabs.extract.markdown(url);
} catch (error) {
// Handle error
}
2. Handle Specific Errors First
Check for specific error types before generic ones:
// ✅ Good: Specific to generic
try {
// API call
} catch (error) {
if (error instanceof UnauthorizedError) {
// Handle auth error
} else if (error instanceof InvalidURLError) {
// Handle URL error
} else if (error instanceof TABStackError) {
// Handle other API errors
} else {
// Handle unexpected errors
}
}
3. Implement Retry Logic for Transient Errors
Retry on server errors, but not on client errors:
// ✅ Good: Retry server errors only
if (error instanceof ServerError || error instanceof ServiceUnavailableError) {
// Implement retry with backoff
} else if (error instanceof UnauthorizedError || error instanceof BadRequestError) {
// Don't retry - fix the issue first
throw error;
}
4. Log Errors for Debugging
Always log errors with context:
// ✅ Good: Contextual logging
try {
const result = await tabs.extract.json(url, schema);
} catch (error) {
console.error(`Failed to extract from ${url}:`, {
errorType: error.constructor.name,
message: error.message,
url,
timestamp: new Date().toISOString()
});
}
5. Provide User-Friendly Messages
Transform technical errors into actionable messages:
// ✅ Good: User-friendly messages
try {
const result = await tabs.extract.markdown(url);
} catch (error) {
if (error instanceof UnauthorizedError) {
return 'Your API key is invalid. Please check your credentials and try again.';
} else if (error instanceof InvalidURLError) {
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.';
}
}
6. Clean Up Resources on Error
Ensure resources are cleaned up even when errors occur:
// ✅ Good: Use finally for cleanup
let logFile;
try {
logFile = openLog();
const result = await tabs.extract.markdown(url);
writeLog(logFile, result);
} catch (error) {
console.error('Error:', error);
throw error;
} finally {
if (logFile) {
closeLog(logFile);
}
}
Common Error Scenarios
Invalid API Key
// Error: UnauthorizedError
// Solution: Verify API key is correct and active
try {
const result = await tabs.extract.markdown(url);
} catch (error) {
if (error instanceof UnauthorizedError) {
console.error('Please set a valid API key in TABSTACK_API_KEY environment variable');
process.exit(1);
}
}
URL Cannot Be Accessed
// Error: InvalidURLError
// Solution: Check URL is valid and publicly accessible
try {
const result = await tabs.extract.markdown('https://localhost:3000');
} catch (error) {
if (error instanceof InvalidURLError) {
console.error('Cannot access private URLs like localhost');
}
}
Schema Mismatch
// Error: ServerError (failed to generate JSON)
// Solution: Adjust schema to match page content
try {
const result = await tabs.extract.json(url, incompatibleSchema);
} catch (error) {
if (error instanceof ServerError && error.message.includes('failed to generate')) {
console.error('Schema does not match page content. Try generating a schema first.');
}
}
Next Steps
- Quickstart: Get started with the SDK
- Extract Features: Learn about data extraction
- Generate Features: Discover AI-powered transformations
- Automate Features: Execute browser automation
- REST API Documentation: View REST API error responses